其他正在播放音频的应用就会被终止播放金沙js娱乐场官方网站,允许在后台获取GPS的描述

查看了一下手机上对应用的定位权限界面,允许在后台获取GPS的描述,而IM语音聊天即是使用语音音频来代替传统文字交流的方式进行沟通交流,于是也难免于俗套使用先录制后发送的方式实现语音聊天,demo下载,其他正在播放音频的应用就会被终止播放,我们注意到有些应用打开音频播放时,本文想从一个原生开发的角度去实现一个简单的语音聊天功能,语音聊天在MMORPG中显得尤为重要

金沙js娱乐场官方网站 7

IOS开发Q&A-IOS8定位应用定位失败及音频录制的相关参数,ios

一、

问题描述:使用xcode6和ios8开发定位应用时,发现执行操作之后,不会调用到定位之后的delegate方法中。查看了一下手机上对应用的定位权限界面,发现我的应用的访问用户的地理位置的权限是空的,即使设置了定位权限依然不会生效。当查阅了相关资料以后,得到如下解决方案:

解决方案:

step1:

在 info.plist里加入:
NSLocationWhenInUseDescription,允许在前台获取GPS的描述
NSLocationAlwaysUsageDescription,允许在后台获取GPS的描述

最终如下图所示:

 

 

step2:

[email protected]里:
CLLocationManager *locationManager;

  1. 初始化:
    locationManager = [[CLLocationManager alloc] init];
  2. 调用请求:
    // 判断是否iOS 8
    if([self.locationManager
    respondsToSelector:@selector(requestAlwaysAuthorization)]) {
    [self.locationManager requestAlwaysAuthorization]; // 永久授权
    [self.locationManager requestWhenInUseAuthorization]; //使用中授权
    }
    [self.locationManager startUpdatingLocation];

添加以上内容之后即可以进行定位服务,百度地图和高德地图测试可用。

参考资料&扩展阅读

 

二、

问题描述:最近要开发一个类似微信的demo,需要支持用户语音对话,这个客户端有IOS版本和Android版本,要完成语音通信功能,在网上看了下资料,类似这类产品使用的技术,talkbox
Android版用的是ilbc的第三方编解码库,在iPhone上用的是caf。微信Android版估计是amr估计转码的是交给腾讯强大的服务器了;米聊Android版和Iphone版用的都是speex。目前支持的开源第三方库有:libopencore_amr,ilbc和speex。

解决方案:

这里我采用libopencore_amr。此库可AMR和WAV音频格式的互转,实现使用iphone录制wav格式,转成amr格式和amr转wav。以便和安卓进行语音聊天。支持architecture
i386, x86_64, armv7, armv7s, arm64。

主要思路:

实现过程是先录制WAV,然后再转AMR,然后发送给Andriod。接收时再把AMR格式转WAV然后播放。

step1.录制WAV,参考了官方的SpeakHere范例

IOS提供的AVFoundation框架可以实现大部分系统声音服务不支持的超过30秒的音频播放功能,同时还提供了录音功能。而我们主要使用到的是AVAudioRecorder与AVAudioPlayer两个类,通过名字我们就可以判断出,前者是提供音频录制服务而后者则是提供播放服务。AVAudioRecorder以各种不同的格式将声音录制到内存或设备本地文件中。录音过程可再应用程序执行其他功能时持续进行。而AVAudioPlayer能够播放任意长度的音频。使用这个类可以实现游戏配乐和其他复杂的音频应用程序。可以全名控制播放过程,包括同时播放多个音频文件等。无疑IOS提供的音频服务是强大以及便利的。再使用AVFoundation框架之前必须要将AVFoundation.framework与CoreAudio.framework加入到项目中,再导入两个接口文件。

#import<AVFoundation/AVFoundation.h>

#import<CoreAudio/CoreAudioTypes.h>

具体的使用实例代码如下,首先是音频录制的使用方法:

 

然后是播放音频的部分:

 

现在我我们来详细解读一下者两段代码的含义,首先是音频录制的代码,我们先后声明并且定义了一下几样东西,创建音频的参数键值对MyRecordParam,一个路径数组pathArray,一个Docment路径字符串DocmentPath以及我们这一步的主角AVAudioRecorder对象MyRecorder。

我们先来解释一下路径的获取,至于音频参数,重头戏需要放在后面不是么~

NSSearchPathForDirectoriesInDomains是IOS中一个搜索路径的方法,它三个参数前两个为枚举,而最后一个参数为BOOL类型,第一个参数的枚举列表如下:

enum {

NSApplicationDirectory = 1,//Supported applications (/Applications)

NSDemoApplicationDirectory,//Unsupported applications and demonstration
versions

NSDeveloperApplicationDirectory,//Developer applications
(/Developer/Applications)

NSAdminApplicationDirectory,//System and network administration
applications

NSLibraryDirectory,//Various user-visible documentation, support, and
configuration files (/Library)

NSDeveloperDirectory,//Developer resources (/Developer)

NSUserDirectory,//User home directories (/Users)

NSDocumentationDirectory,//

NSDocumentDirectory,//

NSCoreServiceDirectory,//Location of core services
(System/Library/CoreServices)

NSAutosavedInformationDirectory = 11,//Location of user’s autosaved
documents Library/Autosave Information

NSDesktopDirectory = 12,//

NSCachesDirectory = 13,//Location of discardable cache files
(Library/Caches)

NSApplicationSupportDirectory = 14,//Location of application support
files (Library/Application Support)

NSDownloadsDirectory = 15,//

NSInputMethodsDirectory = 16,//

NSMoviesDirectory = 17,//

NSMusicDirectory = 18,//

NSPicturesDirectory = 19,//

NSPrinterDescriptionDirectory = 20,//

NSSharedPublicDirectory = 21,//

NSPreferencePanesDirectory = 22,//

NSItemReplacementDirectory = 99,//

NSAllApplicationsDirectory = 100,//

NSAllLibrariesDirectory = 101//

};

其每一项代表一种希望获取到的目录类型,这其中不只是IOS中的目录类型,也有MAC下的路径类型,没错,就跟你想的一样,这个函数并非IOS下专用。

第二个参数的枚举列表如下

enum {

NSUserDomainMask = 1,//用户主目录中

NSLocalDomainMask = 2,//当前机器中

NSNetworkDomainMask = 4,//网络中可见的主机

NSSystemDomainMask = 8,//系统目录,不可修改(/System)

NSAllDomainsMask = 0x0ffff,//全部

};

第二个参数代表要搜索路径的位置,本机?亦或是当前程序,还是局域网连接到的其他电脑。

第三个参数是一个BOOL值他代表是否将返回完整路径

而返回的路径中并不包含文件名,我们一定要记住再路径结尾处加上我们想要的文件名,别忘了我们是要创建一个音频文件。

当然此函数搜索的结果可能又很多条路径,因为根据你的参数不同他返回的路径甚至可能包含其他电脑上的(具体本人未测,有心人可进一步测试,也希望其讲结果与大家分享)所以他的结果是一个数组,而我们要取得的路径目的极为明确,就是程序的Docment路径,并且可以更加肯定是我们的程序只有一个Docment路径,所以我们直接取得了第一条返回记录。

不得不说的是AVAudioRecorder的设计者是个好人,没错,他没有将构造函数的参数设置成一大堆参数,那让人看起来头疼,但其实他用了一个更加让人头疼的方法,没错他让你去手动设置一个参数键值对,你甚至不知道建值对中该填什么参数,哪些参数…这对于习惯看参数列表直接调用方法的人无疑是个噩梦(尤其是当他们英文文档阅读能力低下时-
-),目前我所掌握的参数键的相关资料如下:

AVSampleRateKey, //采样率

AVFormatIDKey,//音频编码格式

AVLinearPCMBitDepthKey,//采样位数 默认 16

AVNumberOfChannelsKey,//通道的数目

AVLinearPCMIsBigEndianKey,//大端还是小端 是内存的组织方式

AVLinearPCMIsFloatKey,//采样信号是整数还是浮点数

AVEncoderAudioQualityKey,//音频编码质量

鉴于考虑到可能各位看官对于我们所要给出的参数并不了解,所以在此我们来依次解释一下每一个参数的含义

首先是采样率,简单地说就是通过波形采样的方法记录1秒钟长度的声音,需要多少个数据。44KHz采样率的声音就是要花费44000个数据来描述1秒钟的声音波形。原则上采样率越高,声音的质量越好。

编码格式可以理解为每种音频格式不同的编解码方式,鄙人对于此了解的也不是非常多(能熟知所有编解码的人一定是偶像级的超人!)而IOS下这些编码方式被集中到一个枚举中,而我们本次代码中所使用的编码格式是WAV文件的格式,想要使用其他的编码格式就在成功导入AVFouncation框架之后即可通过Xcode的自动提示找到以kAudioFormat开头的各种枚举的名称。

采样位数即采样值或取样值,是用来衡量声音波动变化的参数,是指声卡在采集和播放声音文件时所使用数字声音信号的二进制位数。声卡的位客观地反映了数字声音信号对输入声音信号描述的准确程度。

通道数目应该很好理解了,1意味着单声道声音,2指立体声,4是指四个声道等等。

接下来的AVLinearPCMIsBigEndianKey是指再内存中音频的存储模式,在计算机中,通常采用的字节存储机制主要有两种:big-endian和little-endian,即大端模式和小端模式。这个参数为BOOL值,YES为大端,NO为小端。关于大端和小端相关到两个关键词,MSB以及LSB。MSB:Most
Significant Bit( 最高有效位),LSB:Least Significant Bit
(最低有效位)你可以理解为一段数据再内存中的起始位置以及终止位置,大端模式就是MSB存放在最低端的地址上。而小端口模式就是LSB存放在最低端的地址上。

在Big-Endian中,对于bit序列中的序号编排方式如下(以双字节数0x8B8A为例):
bit | casino 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15
——MSB———————————-LSB
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
——————————————–

在Little-Endian中,对于bit序列中的序号编排和Big-Endian刚好相反,其方式如下(以双字节数0x8B8A为例):

bit | 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0
——MSB———————————–LSB
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
———————————————

总之可以理解为在内存中正反两种存储顺序。

对于采样信号是整数还是浮点数也是一个BOOL类型的参数,本人并未理解会影响到的含义,或许是设计到音频信号再解析时候的精准度但并不敢确定还有待设定,所以也没有设置这个参数。

最后一个参数音频编码质量比较好理解了,这个参数又是一个枚举,我们可以找到以AVAudioQuality开头的High、Low、Medium、Max、Min五种设置。介于参数键值对的解释暂时就是这些了,如果又看官得到了一些其他参数的资料或者对于我的解释有纠正补充的欢迎指教。

 

AVAudioRecorder构造函数中的最后一个参数为一个出参,用以保存音频录制的错误信息,如果不想保存错误的信息直接设置为空即可

接下来我们的AVAudioRecorder对象好像看起来成功创建完成了,慢着,不对,为什么会提示错误?仔细看一下,原来是路径的问题,别担心,其实只是AVAudioRecorder跟你开了个小玩笑,因为他实在太懒了,都懒得把字符串路径转换成URL了,所以我们得手动转换一下~没错使用NSURL的静态方法
urlWithString即可解决~接下来只要调用record方法即可开始录音。而以上代码只是实例,建议在编写程序的时候,我们的AVAudioRecorder对象要声名在类内属性中。否则需要结束录音时无法调用到AVAudioRecorder的stop方法。

AVAudioRecorder对象创建起来或许会比较麻烦,但是使用起来确很方便,只要再录音开始时调用record,暂停的时候调用pause方法,而结束的时候调用stop方法就可以了。

相对于录音,播放音频可以说简单的可以,我们在创建AVAudioPlayer对象的时候只需要将之前录音的文件路径给它并且给一个空的错误出参便可轻松的创建出一个AVAudioPlayer对象,使用起来也是那么的方便只要调用一下play函数即可~~~他和AVAudioRecorder一样也可以提供暂停和停止的功能,那它是否可以支持进度调节呢?想知道的话不如用自动提示打开他的方法列表看看呢~一切看起来好像都很简单,我们就要轻松愉快的大功告成了,可当你运行程序之后却惊奇的发现为什么播放不出声音!使用iTools一类的软件检查一下程序的Docment目录,没错啊!文件在啊~,莫非我录制错了?等等等等上万种可能就这样出现在你的脑中!好吧我真不忍心看你像我一样没头没脑的研究几个小时只是因为你没有把实例代码中AVAudioPlayer的声明放到类属性中。。。没错,如果你再某个方法中声明了它并且调用播放函数你就会发现怎么样也播放不出声音,如果你在调用play方法的位置设置下断点再仔细听的话可能会听到一小段声音,为什么呢?因为你刚调用了play的方法你的AVAudioPlayer对象就被释放了,当然什么也没有了也就播放不出声音了~使用AVAudioPlayer就把它的对象放入类内属性吧!

 

这部分转自                      
  

 

一、
问题描述:使用xcode6和ios8开发定位应用时,发现执行操作之后,不会调用到…

import

开始录音的代码:

    NSString * url = NSTemporaryDirectory();
    url = [url stringByAppendingString:[NSString stringWithFormat:@"%f.wav", [[NSDate date] timeIntervalSince1970]]];
    NSMutableDictionary * settings = @{}.mutableCopy;
    [settings setObject:[NSNumber numberWithFloat:8000.0] forKey:AVSampleRateKey];
    [settings setObject:[NSNumber numberWithInt: kAudioFormatLinearPCM] forKey:AVFormatIDKey];  
    [settings setObject:@1 forKey:AVNumberOfChannelsKey];//设置成一个通道,iPnone只有一个麦克风,一个通道已经足够了
    [settings setObject:@16 forKey:AVLinearPCMBitDepthKey];//采样的位数
    self.audioRecorder = [[AVAudioRecorder  alloc] initWithURL:[NSURL fileURLWithPath:url] settings:settings error:&error];
    self.audioRecorder.delegate = self;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
    self.audioRecorder.meteringEnabled = YES;
    BOOL success = [self.audioRecorder record];
    if (success) {
        NSLog(@"录音开始成功");
    }else{
        NSLog(@"录音开始失败");
    }

音乐播放

如果播放较大的音频或者要对音频有精确的控制则System Sound
Service可能就很难满足实际需求了,通常这种情况会选择使用AVFoundation.framework中的AVAudioPlayer来实现。AVAudioPlayer可以看成一个播放器,它支持多种音频格式,而且能够进行进度、音量、播放速度等控制

属性 说明
@property(readonly, getter=isPlaying) BOOL playing 是否正在播放,只读
@property(readonly) NSUInteger numberOfChannels 音频声道数,只读
@property(readonly) NSTimeInterval duration 音频时长
@property(readonly) NSURL *url 音频文件路径,只读
@property(readonly) NSData *data 音频数据,只读
@property float pan 立体声平衡,如果为-1.0则完全左声道,如果0.0则左右声道平衡,如果为1.0则完全为右声道
@property float volume 音量大小,范围0-1.0
@property BOOL enableRate 是否允许改变播放速率
@property float rate 播放速率,范围0.5-2.0,如果为1.0则正常播放,如果要修改播放速率则必须设置enableRate为YES
@property NSTimeInterval currentTime 当前播放时长
@property(readonly) NSTimeInterval deviceCurrentTime 输出设备播放音频的时间,注意如果播放中被暂停此时间也会继续累加
@property NSInteger numberOfLoops 循环播放次数,如果为0则不循环,如果小于0则无限循环,大于0则表示循环次数
@property(readonly) NSDictionary *settings 音频播放设置信息,只读
@property(getter=isMeteringEnabled) BOOL meteringEnabled 是否启用音频测量,默认为NO,一旦启用音频测量可以通过updateMeters方法更新测量值
对象方法 说明
-(instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError 使用文件URL初始化播放器,注意这个URL不能是HTTP URL,AVAudioPlayer不支持加载网络媒体流,只能播放本地文件,如果需要播放网络URL,可以尝试AVPlayer
-(BOOL)prepareToPlay 加载音频文件到缓冲区,注意即使在播放之前音频文件没有加载到缓冲区程序也会隐式调用此方法。
-(BOOL)play 播放音频文件
-(BOOL)playAtTime:(NSTimeInterval)time 在指定的时间开始播放音频
-(void)pause 暂停播放
-(void)stop 停止播放
-(void)updateMeters 更新音频测量值,注意如果要更新音频测量值必须设置meteringEnabled为YES,通过音频测量值可以即时获得音频分贝等信息
-(float)peakPowerForChannel:(NSUInteger)channelNumber 获得指定声道的分贝峰值,注意如果要获得分贝峰值必须在此之前调用updateMeters方法
-(float)averagePowerForChannel:(NSUInteger)channelNumber 获得指定声道的分贝平均值,注意如果要获得分贝平均值必须在此之前调用updateMeters方法
代理方法 说明
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag 音频播放完成
-(void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error 音频解码发生错误
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player 播放被打断
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player 结束打断

使用步骤:

  1. 初始化AVAudioPlayer对象,此时需要指定本地文件路径
  2. 设置播放器属性,例如重复次数、音量大小等
  3. 调用play方法播放

示例:

-(void)playAudioWithUrl:(NSURL*)url{
    NSError *error=nil;
    // 1、初始化
    _audioPlayer=[[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    // 2、设置相关属性
    _audioPlayer.numberOfLoops=0;   // 设置为0不循环
    _audioPlayer.delegate = self;
    if (error) {
        NSLog(@"创建播放器过程中发生错误,错误信息:%@",error.localizedDescription);
    }
    if (![_audioPlayer isPlaying]) {
        //解决音量小的问题
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        NSError *err = nil;
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:&err];
        // 3、播放
        [_audioPlayer play];    // 播放音频
    }
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    NSLog(@"音乐播放完成...");
}

[TOC]

3.怎么转换编码问题

最近的项目的内容开始涉及到一定的IM语音对讲的内容,而笔者从未接触过此类开发,也只是在摸索中一点点探索学习,几日下来略有了一点收获,以博客的形式跟诸位看官分享
先说明一下什么是IM语音聊天,IM全称Instant
Messenger,即时通讯,简单的来说就是MSN
,QQ一类的聊天软件。而IM语音聊天即是使用语音音频来代替传统文字交流的方式进行沟通交流,目前市面上的语音IM根据聊天的方式又几种不同的方式,一种是即时发言犹如电话通信一样的语音通信,其中比较具有代表的软件有UCTalk,YY语音等,此类语音软件以PC平台为主,另外一种则是先进行录音之后发送形式与传统文字IM的形式略有相似之处的聊天方式,比较具有代表性的软件有微信,陌陌等,此类的语音软件大多出现在移动端,PC平台上同时也具有少量的使用。还有一种则可以视为介于两者中间的,模仿传统对讲机形式进行轮流发言的聊天方式,这种方式只在PC台上少量使用出现过,较前两者使用的范围比较小。
本次我们做的是移动端的项目,于是也难免于俗套使用先录制后发送的方式实现语音聊天,鄙人才疏学浅了解不深,所以可能是主观上的臆断,感觉以录制发送的形式实现的语音IM实现起来从技术将要简单很多,毕竟不会设计流媒体的问题。
既然是语音聊天难免涉及到一些音频相关的问题,笔者是负责IOS端开发的,所以大部分的内容以IOS角度为主,IOS提供的AVFoundation框架可以实现大部分系统声音服务不支持的超过30秒的音频播放功能,同时还提供了录音功能。而我们主要使用到的是AVAudioRecorder与AVAudioPlayer两个类,通过名字我们就可以判断出,前者是提供音频录制服务而后者则是提供播放服务。AVAudioRecorder以各种不同的格式将声音录制到内存或设备本地文件中。录音过程可再应用程序执行其他功能时持续进行。而AVAudioPlayer能够播放任意长度的音频。使用这个类可以实现游戏配乐和其他复杂的音频应用程序。可以全名控制播放过程,包括同时播放多个音频文件等。无疑IOS提供的音频服务是强大以及便利的。再使用AVFoundation框架之前必须要将AVFoundation.framework与CoreAudio.framework加入到项目中,再导入两个接口文件。

获取录音过程中的分贝

要在录音过程中获取分贝数,要在录音前先把AVAudioRecorder的属性meteringEnabled设置成YES.
在录音开始后,可以设置一个定时器获取分贝数

_metesTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(setVoiceImage) userInfo:nil repeats:YES];
-(void)setVoiceImage{
    if (self.audioRecorder.isRecording) {
        [self.audioRecorder updateMeters];
        float peakPower = [self.audioRecorder peakPowerForChannel:0];
        NSLog(@"%f", peakPower);
    }
}

AVAudioRecorderaveragePowerForChannelpeakPowerForChannel方法返回的是分贝数据,数值在-160
– 0之间

音效播放

AudioToolbox.framework是一套基于C语言的框架,使用它来播放音效其本质是将短音频注册到系统声音服务(System
Sound Service)。System Sound
Service是一种简单、底层的声音播放服务,但是它本身也存在着一些限制:

  1. 音频播放时间不能超过30s
  2. 数据必须是PCM或者IMA4格式
  3. 音频文件必须打包成.caf、.aif、.wav中的一种

使用步骤如下:

  1. 调用AudioServicesCreateSystemSoundID函数来获取系统声音ID
  2. 调用AudioServicesPlaySystemSound或者AudioServicesPlayAlertSound
    方法播放音效(后者带有震动效果)
  3. 如果需要监听播放完成操作,则使用AudioServicesAddSystemSoundCompletion方法注册回调函数

例子:

-(void)playAudioWithName:(NSString*)soundName{
    NSURL* system_sound_url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:soundName ofType:nil]];
    //1.获取系统声音ID
    SystemSoundID system_sound_id = 0;
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)system_sound_url,&system_sound_id);
    //需要播放完成之后执行某些操作,可以调用下面方法注册一个播放完成回调函数
/*    AudioServicesAddSystemSoundCompletion(system_sound_id,
                                          NULL, // uses the main run loop
                                          NULL, // uses kCFRunLoopDefaultMode
                                          soundCompleteCallback, // the name of our custom callback function
                                          NULL // for user data, but we don't need to do that in this case, so we just pass NULL
                                          );
*/
    //2.播放音频
//    AudioServicesPlaySystemSound(system_sound_id);//播放音效
    AudioServicesPlayAlertSound(system_sound_id);//播放并震动
}

void soundCompleteCallback(SystemSoundID soundID,void * userData){
 NSLog(@"播放完成");
 //do what you want to do
 AudioServicesDisposeSystemSoundID(soundID);
}

补充:获取系统音效

_systemSounds               = [NSMutableArray array];
// 读取文件系统
NSFileManager *fileManager  = [NSFileManager defaultManager];
NSURL         *directoryURL = [NSURL URLWithString:@"/System/Library/Audio/UISounds"];
NSArray       *keys         = [NSArray arrayWithObject:NSURLIsDirectoryKey];
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:directoryURL
                                      includingPropertiesForKeys:keys
                                                         options:0
                                                    errorHandler:^(NSURL *url, NSError *error) {
                                                        return YES;
                                                    }];
for (NSURL *url in enumerator) {
    NSError  *error;
    NSNumber *isDirectory = nil;
    if (! [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) {
    } else if (![isDirectory boolValue]) {
        SystemSoundID soundID;
        AudioServicesCreateSystemSoundID((__bridge_retained CFURLRef)url, &soundID);
        // 音效的数据模型
        SoundInfomation *sound = [[SoundInfomation alloc] init];
        sound.soundID   = soundID;
        sound.soundUrl  = url;
        sound.soundName = url.lastPathComponent;
        [_systemSounds addObject:sound];
    }
}

[TOC]

2.当从语音服务器下载arm的语音文件后,先把语音文件转为wav格式,再创建AVAudioPlayer对象进行播放

import

具体的使用实例代码如下,首先是音频录制的使用方法:

金沙js娱乐场官方网站 1

然后是播放音频的部分:

金沙js娱乐场官方网站 2

现在我我们来详细解读一下者两段代码的含义,首先是音频录制的代码,我们先后声明并且定义了一下几样东西,创建音频的参数键值对MyRecordParam,一个路径数组pathArray,一个Docment路径字符串DocmentPath以及我们这一步的主角AVAudioRecorder对象MyRecorder。
我们先来解释一下路径的获取,至于音频参数,重头戏需要放在后面不是么~
NSSearchPathForDirectoriesInDomains是IOS中一个搜索路径的方法,它三个参数前两个为枚举,而最后一个参数为BOOL类型,第一个参数的枚举列表如下:
enum {
NSApplicationDirectory = 1,//Supported applications (/Applications)
NSDemoApplicationDirectory,//Unsupported applications and demonstration
versions
NSDeveloperApplicationDirectory,//Developer applications
(/Developer/Applications)
NSAdminApplicationDirectory,//System and network administration
applications
NSLibraryDirectory,//Various user-visible documentation, support, and
configuration files (/Library)
NSDeveloperDirectory,//Developer resources (/Developer)
NSUserDirectory,//User home directories (/Users)
NSDocumentationDirectory,//
NSDocumentDirectory,//
NSCoreServiceDirectory,//Location of core services
(System/Library/CoreServices)
NSAutosavedInformationDirectory = 11,//Location of user’s autosaved
documents Library/Autosave Information
NSDesktopDirectory = 12,//
NSCachesDirectory = 13,//Location of discardable cache files
(Library/Caches)
NSApplicationSupportDirectory = 14,//Location of application support
files (Library/Application Support)
NSDownloadsDirectory = 15,//
NSInputMethodsDirectory = 16,//
NSMoviesDirectory = 17,//
NSMusicDirectory = 18,//
NSPicturesDirectory = 19,//
NSPrinterDescriptionDirectory = 20,//
NSSharedPublicDirectory = 21,//
NSPreferencePanesDirectory = 22,//
NSItemReplacementDirectory = 99,//
NSAllApplicationsDirectory = 100,//
NSAllLibrariesDirectory = 101//
};
其每一项代表一种希望获取到的目录类型,这其中不只是IOS中的目录类型,也有MAC下的路径类型,没错,就跟你想的一样,这个函数并非IOS下专用。
第二个参数的枚举列表如下
enum {
NSUserDomainMask = 1,//用户主目录中
NSLocalDomainMask = 2,//当前机器中
NSNetworkDomainMask = 4,//网络中可见的主机
NSSystemDomainMask = 8,//系统目录,不可修改(/System)
NSAllDomainsMask = 0x0ffff,//全部
};
第二个参数代表要搜索路径的位置,本机?亦或是当前程序,还是局域网连接到的其他电脑。
第三个参数是一个BOOL值他代表是否将返回完整路径
而返回的路径中并不包含文件名,我们一定要记住再路径结尾处加上我们想要的文件名,别忘了我们是要创建一个音频文件。
当然此函数搜索的结果可能又很多条路径,因为根据你的参数不同他返回的路径甚至可能包含其他电脑上的(具体本人未测,有心人可进一步测试,也希望其讲结果与大家分享)所以他的结果是一个数组,而我们要取得的路径目的极为明确,就是程序的Docment路径,并且可以更加肯定是我们的程序只有一个Docment路径,所以我们直接取得了第一条返回记录。
不得不说的是AVAudioRecorder的设计者是个好人,没错,他没有将构造函数的参数设置成一大堆参数,那让人看起来头疼,但其实他用了一个更加让人头疼的方法,没错他让你去手动设置一个参数键值对,你甚至不知道建值对中该填什么参数,哪些参数…这对于习惯看参数列表直接调用方法的人无疑是个噩梦(尤其是当他们英文文档阅读能力低下时-
-),目前我所掌握的参数键的相关资料如下:
AVSampleRateKey, //采样率
AVFormatIDKey,//音频编码格式
AVLinearPCMBitDepthKey,//采样位数 默认 16
AVNumberOfChannelsKey,//通道的数目
AVLinearPCMIsBigEndianKey,//大端还是小端 是内存的组织方式
AVLinearPCMIsFloatKey,//采样信号是整数还是浮点数
AVEncoderAudioQualityKey,//音频编码质量
鉴于考虑到可能各位看官对于我们所要给出的参数并不了解,所以在此我们来依次解释一下每一个参数的含义
首先是采样率,简单地说就是通过波形采样的方法记录1秒钟长度的声音,需要多少个数据。44KHz采样率的声音就是要花费44000个数据来描述1秒钟的声音波形。原则上采样率越高,声音的质量越好。
编码格式可以理解为每种音频格式不同的编解码方式,鄙人对于此了解的也不是非常多(能熟知所有编解码的人一定是偶像级的超人!)而IOS下这些编码方式被集中到一个枚举中,而我们本次代码中所使用的编码格式是WAV文件的格式,想要使用其他的编码格式就在成功导入AVFouncation框架之后即可通过Xcode的自动提示找到以kAudioFormat开头的各种枚举的名称。
采样位数即采样值或取样值,是用来衡量声音波动变化的参数,是指声卡在采集和播放声音文件时所使用数字声音信号的二进制位数。声卡的位客观地反映了数字声音信号对输入声音信号描述的准确程度。
通道数目应该很好理解了,1意味着单声道声音,2指立体声,4是指四个声道等等。
接下来的AVLinearPCMIsBigEndianKey是指再内存中音频的存储模式,在计算机中,通常采用的字节存储机制主要有两种:big-endian和little-endian,即大端模式和小端模式。这个参数为BOOL值,YES为大端,NO为小端。关于大端和小端相关到两个关键词,MSB以及LSB。MSB:Most
Significant Bit( 最高有效位),LSB:Least Significant Bit
(最低有效位)你可以理解为一段数据再内存中的起始位置以及终止位置,大端模式就是MSB存放在最低端的地址上。而小端口模式就是LSB存放在最低端的地址上。
在Big-Endian中,对于bit序列中的序号编排方式如下(以双字节数0x8B8A为例):bit
| casino
0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15——MSB———————————-LSB
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |——————————————–
在Little-Endian中,对于bit序列中的序号编排和Big-Endian刚好相反,其方式如下(以双字节数0x8B8A为例):
bit | 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0——MSB———————————–LSBval | 1
0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |———————————————
总之可以理解为在内存中正反两种存储顺序。
对于采样信号是整数还是浮点数也是一个BOOL类型的参数,本人并未理解会影响到的含义,或许是设计到音频信号再解析时候的精准度但并不敢确定还有待设定,所以也没有设置这个参数。
最后一个参数音频编码质量比较好理解了,这个参数又是一个枚举,我们可以找到以AVAudioQuality开头的High、Low、Medium、Max、Min五种设置。介于参数键值对的解释暂时就是这些了,如果又看官得到了一些其他参数的资料或者对于我的解释有纠正补充的欢迎指教。

AVAudioRecorder构造函数中的最后一个参数为一个出参,用以保存音频录制的错误信息,如果不想保存错误的信息直接设置为空即可
接下来我们的AVAudioRecorder对象好像看起来成功创建完成了,慢着,不对,为什么会提示错误?仔细看一下,原来是路径的问题,别担心,其实只是AVAudioRecorder跟你开了个小玩笑,因为他实在太懒了,都懒得把字符串路径转换成URL了,所以我们得手动转换一下~没错使用NSURL的静态方法
urlWithString即可解决~接下来只要调用record方法即可开始录音。而以上代码只是实例,建议在编写程序的时候,我们的AVAudioRecorder对象要声名在类内属性中。否则需要结束录音时无法调用到AVAudioRecorder的stop方法。
AVAudioRecorder对象创建起来或许会比较麻烦,但是使用起来确很方便,只要再录音开始时调用record,暂停的时候调用pause方法,而结束的时候调用stop方法就可以了。
相对于录音,播放音频可以说简单的可以,我们在创建AVAudioPlayer对象的时候只需要将之前录音的文件路径给它并且给一个空的错误出参便可轻松的创建出一个AVAudioPlayer对象,使用起来也是那么的方便只要调用一下play函数即可~~他和AVAudioRecorder一样也可以提供暂停和停止的功能,那它是否可以支持进度调节呢?想知道的话不如用自动提示打开他的方法列表看看呢一切看起来好像都很简单,我们就要轻松愉快的大功告成了,可当你运行程序之后却惊奇的发现为什么播放不出声音!使用iTools一类的软件检查一下程序的Docment目录,没错啊!文件在啊,莫非我录制错了?等等等等上万种可能就这样出现在你的脑中!好吧我真不忍心看你像我一样没头没脑的研究几个小时只是因为你没有把实例代码中AVAudioPlayer的声明放到类属性中。。。没错,如果你再某个方法中声明了它并且调用播放函数你就会发现怎么样也播放不出声音,如果你在调用play方法的位置设置下断点再仔细听的话可能会听到一小段声音,为什么呢?因为你刚调用了play的方法你的AVAudioPlayer对象就被释放了,当然什么也没有了也就播放不出声音了使用AVAudioPlayer就把它的对象放入类内属性吧!
wav没错我们的成功的录制并播放了一个wav,就在你开心的时候你会发现一个问题,纳尼!?安卓不支持wav!!!这可怎么办?难道要让本来就少的可怜的用户还要永隔安卓与IOS的大洋两岸么!一切问题都会有解决方法的!但是,预知后事如何,请听下回分解

停止录音

    [self.audioRecorder stop];

调用这个方法后,会走下面的代理方法

-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
    NSURL * url = recorder.url;
}

音频管理

下面是笔者自己写的一个音频管理类。可以进行音频录制、播放、获取音频信息、管理音频文件等功能

.h 文件

//
//  AudioManager.h
//  ExeToExp
//
//  Created by LOLITA on 17/3/28.
//  Copyright © 2017年 LOLITA. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@protocol AudioManagerDelegate;
@interface AudioManager : NSObject
@property (nonatomic,weak) id <AudioManagerDelegate> delegate;

+(instancetype)sharedInstance;

// !!!: 开始录制
-(void)startRecord;

// !!!: 暂停录制
-(void)pauseRecord;

// !!!: 恢复录制
-(void)resumeRecord;

// !!!: 停止录制
-(void)stopRecord;

// !!!: 取消当前录制
-(void)cancelRecord;

// !!!: 播放语音
-(void)playAudioWithUrl:(NSURL*)url;

// !!!: 停止语音播放
-(void)stopPlay;

// !!!: 暂停语音播放
-(void)pausePlay;

// !!!: 恢复语音播放
-(void)resumePlay;

// !!!: 获取当前录制文件的路径
-(NSURL*)recordCurrentAudioFile;

// !!!: 获取语音时长
-(float)durationWithAudio:(NSURL *)audioUrl;

// !!!: 删除本地音频文件下所有文件
-(void)removeAllAudioFile;

// !!!: 删除本地指定音频文件
-(void)removeAudioFile:(NSURL*)url;

// !!!: 删除指定后缀的文件,如“.wav”,“.caf”
-(void)removeFileSuffixList:(NSArray<NSString*>*)suffixList filePath:(NSString*)path;
@end

@protocol AudioManagerDelegate <NSObject>
@optional
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder*)recorder successfullyFlag:(BOOL)flag;  // 录制完成
-(void)audioPowerChange:(CGFloat)power; // 音量
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag; // 播放完成
@end

.m 文件

//
//  AudioManager.m
//  ExeToExp
//
//  Created by LOLITA on 17/3/28.
//  Copyright © 2017年 LOLITA. All rights reserved.
//

#define kAudioFolder @"AudioFolder" // 音频文件夹
#import "AudioManager.h"
@interface AudioManager ()<AVAudioRecorderDelegate,AVAudioPlayerDelegate>
@property (nonatomic,strong) AVAudioRecorder *audioRecorder;    // 录音机
@property (nonatomic,strong) AVAudioPlayer *audioPlayer;        // 音频播放器
@property (strong ,nonatomic) NSDictionary *setting;            // 录音机的设置
@property (copy ,nonatomic) NSString *audioDir;                 // 录音文件夹路径
@property (nonatomic,strong) NSTimer *timer;    // 录音声波监控
@property (copy ,nonatomic) NSString *filename; // 记录当前文件名
@property (assign ,nonatomic) BOOL cancelCurrentRecord;    // 取消当前录制
@end;

@implementation AudioManager
+(instancetype)sharedInstance{
    static AudioManager *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[AudioManager alloc] init];
    });
    return instance;
}
#pragma mark - <************************** 一些初始化 **************************>
// !!!: 配置录音机
-(void)setupRecorder{
    //设置音频会话
    NSError *sessionError;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:&sessionError];
    if (sessionError){
        NSLog(@"Error creating session: %@",[sessionError description]);
    }else{
        [[AVAudioSession sharedInstance] setActive:YES error:&sessionError];
    }
    //录音设置
    //创建录音文件保存路径
    NSURL *url = [self getSavePath];
    //创建录音机
    NSError *error = nil;
    _audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:self.setting error:&error];
    _audioRecorder.delegate = self;
    _audioRecorder.meteringEnabled = YES;//如果要监控声波则必须设置为YES
    [_audioRecorder prepareToRecord];
    if (error) {
        NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription);
    }
}


// !!!: 录音声波监
-(NSTimer *)timer{
    if (!_timer) {
        _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(powerChange) userInfo:nil repeats:YES];
    }
    return _timer;
}

// !!!: 录音设置
-(NSDictionary *)setting{
    if (_setting==nil) {
        NSMutableDictionary *setting = [NSMutableDictionary dictionary];
        //录音格式
        [setting setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
        //采样率,8000/11025/22050/44100/96000(影响音频的质量),8000是电话采样率
        [setting setObject:@(22050) forKey:AVSampleRateKey];
        //通道 , 1/2
        [setting setObject:@(2) forKey:AVNumberOfChannelsKey];
        //采样点位数,分为8、16、24、32, 默认16
        [setting setObject:@(16) forKey:AVLinearPCMBitDepthKey];
        //是否使用浮点数采样
        [setting setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
        // 录音质量
        [setting setObject:@(AVAudioQualityHigh) forKey:AVEncoderAudioQualityKey];
        //....其他设置等
    }
    return _setting;
}

// !!!: 录音文件夹
-(NSString *)audioDir{
    if (_audioDir==nil) {
        _audioDir = NSTemporaryDirectory();
        _audioDir = [_audioDir stringByAppendingPathComponent:kAudioFolder];
        BOOL isDir = NO;
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL existed = [fileManager fileExistsAtPath:_audioDir isDirectory:&isDir];
        if (!(isDir == YES && existed == YES)){
            [fileManager createDirectoryAtPath:_audioDir withIntermediateDirectories:YES attributes:nil error:nil];
        }
    }
    return _audioDir;
}

#pragma mark - <************************** 事件 **************************>
// !!!: 开始录制
-(void)startRecord{
    [self setupRecorder];
    if (![self.audioRecorder isRecording]) {
        [self.audioRecorder record];//首次使用应用时如果调用record方法会询问用户是否允许使用麦克风
//        [self.audioRecorder recordForDuration:60];    // 录音时长
        self.timer.fireDate=[NSDate distantPast];
    }
}
// !!!: 暂停录制
-(void)pauseRecord{
    if ([self.audioRecorder isRecording]) {
        [self.audioRecorder pause];
        self.timer.fireDate=[NSDate distantFuture];
    }
}
// !!!: 恢复录制
-(void)resumeRecord{
    if (![self.audioRecorder isRecording]) {
        [self.audioRecorder record];
        self.timer.fireDate=[NSDate distantPast];
    }
}
// !!!: 停止录制
-(void)stopRecord{
    [self.audioRecorder stop];
    self.timer.fireDate=[NSDate distantFuture];
}

// !!!: 取消当前录制
-(void)cancelRecord{
    self.cancelCurrentRecord = YES;
    [self stopRecord];
    if ([self.audioRecorder deleteRecording]) {
        NSLog(@"删除录音文件!");
    }
}


// !!!: 播放音频文件
-(void)playAudioWithUrl:(NSURL*)url{
    //语音播放
    NSError *error=nil;
    _audioPlayer=[[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    _audioPlayer.numberOfLoops=0;   // 设置为0不循环
    _audioPlayer.delegate = self;
    if (error) {
        NSLog(@"创建播放器过程中发生错误,错误信息:%@",error.localizedDescription);
    }
    if (![_audioPlayer isPlaying]) {
        //解决音量小的问题
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        NSError *err = nil;
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:&err];
        [_audioPlayer play];    // 播放音频
    }
}

// !!!: 停止播放语音
-(void)stopPlay{
    [self.audioPlayer stop];
}

// !!!: 暂停语音
-(void)pausePlay{
    [self.audioPlayer pause];
}

// !!!: 恢复语音
-(void)resumePlay{
    [self.audioPlayer play];
}


#pragma mark - <************************** 获取数据 **************************>
// !!!: 获取录音保存路径
-(NSURL*)getSavePath{
    self.filename = [NSString stringWithFormat:@"audio_%@.wav",[self getDateString]];
    NSString* fileUrlString = [self.audioDir stringByAppendingPathComponent:self.filename];
    NSURL *url = [NSURL fileURLWithPath:fileUrlString];
    return url;
}

// !!!: 返回音频文件地址
-(NSURL *)recordCurrentAudioFile{
    NSString* fileUrlString = [self.audioDir stringByAppendingPathComponent:self.filename];
    NSURL *url = [NSURL fileURLWithPath:fileUrlString];
    return url;
}


// !!!: 获取语音时长
-(float)durationWithAudio:(NSURL *)audioUrl{
    AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:audioUrl options:nil];
    CMTime audioDuration = audioAsset.duration;
    float audioDurationSeconds = CMTimeGetSeconds(audioDuration);
    return audioDurationSeconds;
}


// !!!: 删除所有文件夹
-(void)removeAllAudioFile{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager removeItemAtPath:self.audioDir error:nil]) {
        NSLog(@"删除文件夹成功!!");
    }
}

// !!!: 删除指定文件
-(void)removeAudioFile:(NSURL *)url{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager removeItemAtPath:url.path error:nil]) {
        NSLog(@"删除录音文件成功!!");
    }
}

// !!!: 删除指定后缀的文件
-(void)removeFileSuffixList:(NSArray<NSString *> *)suffixList filePath:(NSString *)path{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *contentOfFolder = [fileManager contentsOfDirectoryAtPath:path error:NULL];
    for (NSString *aPath in contentOfFolder) {
        NSString * fullPath = [path stringByAppendingPathComponent:aPath];
        BOOL isDir = NO;
        if ([fileManager fileExistsAtPath:fullPath isDirectory:&isDir]) {
            if (isDir == YES) {
                // 是文件夹,则继续遍历
                [self removeFileSuffixList:suffixList filePath:fullPath];
            }
            else{
                NSLog(@"file-:%@", aPath);
                for (NSString* suffix in suffixList) {
                    if ([aPath hasSuffix:suffix]) {
                        if ([fileManager removeItemAtPath:fullPath error:nil]) {
                            NSLog(@"删除文件成功!!");
                        }
                    }
                }
            }
        }
    }
}



#pragma mark - <************************** 代理方法 **************************>
// !!!: 录音代理事件
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
    if (self.cancelCurrentRecord) {
        self.cancelCurrentRecord = NO;
        NSLog(@"取消录制!");
    }
    else{
        if (self.delegate&&[self.delegate respondsToSelector:@selector(audioRecorderDidFinishRecording:successfullyFlag:)]) {
            [self.delegate audioRecorderDidFinishRecording:recorder successfullyFlag:flag];
        }
        NSLog(@"录制完成!");
    }
}
// !!!: 播放语音代理事件
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    if (self.delegate&&[self.delegate respondsToSelector:@selector(audioPlayerDidFinishPlaying:successfully:)]) {
        [self.delegate audioPlayerDidFinishPlaying:player successfully:flag];
    }
    NSLog(@"播放完成!");
}


#pragma mark - <************************** 私有方法 **************************>
// !!!: 获取时刻名称
-(NSString*)getDateString{
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    NSInteger unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday |
    NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
    NSDateComponents *comps  = [calendar components:unitFlags fromDate:[NSDate date]];
    NSInteger year = [comps year];
    NSInteger month = [comps month];
    NSInteger day = [comps day];
    NSInteger hour = [comps hour];
    NSInteger min = [comps minute];
    NSInteger sec = [comps second];
    NSString* formatString = @"%d%02d%02d%02d%02d%02d";
    return [NSString stringWithFormat:formatString, year, month, day, hour, min, sec];
}

// !!!: 录音声波状态设置
-(void)powerChange{
    [self.audioRecorder updateMeters];//更新测量值
    float power = [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0
    CGFloat progress = power+160.0;
    NSLog(@"音频强度:%f",power);
    if (self.delegate&&[self.delegate respondsToSelector:@selector(audioPowerChange:)]) {
        [self.delegate audioPowerChange:progress];
    }
}

-(void)dealloc{
    [self removeAllAudioFile];
}

@end

[TOC]

3.怎么转换编码问题

播放语音

可以将录音后获取的url传入下面的方法中:

- (void)playAudioWithURL:(NSURL *)url{
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    NSError * error;
    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    self.audioPlayer.delegate = self;
    BOOL success = [self.audioPlayer play];
    if (success) {
        NSLog(@"播放成功");
    }else{
        NSLog(@"播放失败");
    }
}

音频录制

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

属性 说明
@property(readonly, getter=isRecording) BOOL recording 是否正在录音,只读
@property(readonly) NSURL *url 录音文件地址,只读
@property(readonly) NSDictionary *settings 录音文件设置,只读
@property(readonly) NSTimeInterval currentTime 录音时长,只读,注意仅仅在录音状态可用
@property(readonly) NSTimeInterval deviceCurrentTime 输入设置的时间长度,只读,注意此属性一直可访问
@property(getter=isMeteringEnabled) BOOL meteringEnabled 是否启用录音测量,如果启用录音测量可以获得录音分贝等数据信息
对象方法 说明
-(instancetype)initWithURL:(NSURL *)url settings:(NSDictionary *)settings error:(NSError **)outError 录音机对象初始化方法,注意其中的url必须是本地文件url,settings是录音格式、编码等设置
-(BOOL)prepareToRecord 准备录音,主要用于创建缓冲区,如果不手动调用,在调用record录音时也会自动调用
-(BOOL)record 开始录音/恢复录音
-(BOOL)recordAtTime:(NSTimeInterval)time 在指定将来的某个时刻开始录音
-(BOOL)recordForDuration:(NSTimeInterval) duration 指定的时长开始录音,如60′
-(BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration 在指定的时间开始录音,并指定录音时长
-(void)pause 暂停录音
-(void)stop 停止录音
-(BOOL)deleteRecording 删除录音,注意要删除录音此时录音机必须处于停止状态
-(void)updateMeters 更新测量数据,注意只有meteringEnabled为YES此方法才可用
-(float)peakPowerForChannel:(NSUInteger)channelNumber 指定通道的测量峰值,注意只有调用完updateMeters才有值
-(float)averagePowerForChannel:(NSUInteger)channelNumber 指定通道的测量平均值,注意只有调用完updateMeters才有值
代理方法 说明
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag 完成录音
-(void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error 录音编码发生错误
-(void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder 录音被打断
-(void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder 录音打断结束

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

使用步骤:

  1. 设置音频会话类型为AVAudioSessionCategoryRecord,录音模式
  2. 创建录音机AVAudioRecorder,指定录音保存的路径并且设置录音属性
  3. 调用record开始录制

示例:

-(void)setupRecorder{
    //设置音频会话
    NSError *sessionError;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:&sessionError];
    if (sessionError){
        NSLog(@"Error creating session: %@",[sessionError description]);
    }else{
        [[AVAudioSession sharedInstance] setActive:YES error:&sessionError];
    }
    //录音设置
    //创建录音文件保存路径
    NSURL *url = [self getSavePath];
    //创建录音机
    NSError *error = nil;
    _audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:self.setting error:&error];
    if (error) {
        NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription);
    }
    _audioRecorder.delegate = self;
    _audioRecorder.meteringEnabled = YES;//如果要监控声波则必须设置为YES
    [_audioRecorder prepareToRecord];
    if (![_audioRecorder isRecording]) {
        [_audioRecorder record];//首次使用应用时如果调用record方法会询问用户是否允许使用麦克风
    }
}

// !!!: 录音设置
-(NSDictionary *)setting{
    if (_setting==nil) {
        NSMutableDictionary *setting = [NSMutableDictionary dictionary];
        //录音格式
        [setting setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
        //采样率,8000/11025/22050/44100/96000(影响音频的质量),8000是电话采样率
        [setting setObject:@(22050) forKey:AVSampleRateKey];
        //通道 , 1/2
        [setting setObject:@(2) forKey:AVNumberOfChannelsKey];
        //采样点位数,分为8、16、24、32, 默认16
        [setting setObject:@(16) forKey:AVLinearPCMBitDepthKey];
        //是否使用浮点数采样
        [setting setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
        // 录音质量
        [setting setObject:@(AVAudioQualityHigh) forKey:AVEncoderAudioQualityKey];
        //....其他设置等
    }
    return _setting;
}

[TOC]

private const string IOSSDKDLL = “__Internal”;
#if UNITY_IPHONE
[DllImport(IOSSDKDLL, CallingConvention =
CallingConvention.Cdecl)]
public static extern void
__SendSDKMessage(string methodName,string arg0, string
arg1);

遇到的问题

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];

这个是设置AVAudioSession的category的,如果不设置的话,在模拟器上success是YES,但是在真机上是NO。同样,在用AVAudioPlayer播放语音时要设置category为AVAudioSessionCategoryPlayback

补充:音频队列服务

音频队列服务Audio Queue
Services属于AudioToolbox框架,使用音频队列服务可以做到音频流式播放和录制

首先看一下录音音频服务队列:

金沙js娱乐场官方网站 3

录音音频服务队列

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

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

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

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

声音通过输入设备进入缓冲队列中,首先填充第一个缓冲器;当第一个缓冲器填充满之后自动填充下一个缓冲器,同时会调用回调函数;在回调函数中需要将缓冲器中的音频数据写入磁盘,同时将缓冲器放回到缓冲队列中以便重用。下面是Apple官方关于音频队列服务的流程示意图:

金沙js娱乐场官方网站 4

音频队列服务的流程示意图

类似的,看一下音频播放缓冲队列,其组成部分和录音缓冲队列类似。

金沙js娱乐场官方网站 5

音频播放缓冲队列

但是在音频播放缓冲队列中,回调函数调用的时机不同于音频录制缓冲队列,流程刚好相反。将音频读取到缓冲器中,一旦一个缓冲器填充满之后就放到缓冲队列中,然后继续填充其他缓冲器;当开始播放时,则从第一个缓冲器中读取音频进行播放;一旦播放完之后就会触发回调函数,开始播放下一个缓冲器中的音频,同时填充第一个缓冲器放;填充满之后再次放回到缓冲队列。下面是详细的流程:

金沙js娱乐场官方网站 6

这里写图片描述

当然,要明白音频队列服务的原理并不难,问题是如何实现这个自定义的回调函数,这其中我们有大量的工作要做,控制播放状态、处理异常中断、进行音频编码等等。由于牵扯内容过多,而且不是本文目的,如果以后有时间将另开一篇文章重点介绍,目前有很多第三方优秀框架可以直接使用,例如AudioStreamer、FreeStreamer。(前者当前只有非ARC版本)


[TOC]

//播放

AVAudioRecorder

AVAudioRecorder 的初始化方法是

- (nullable instancetype)initWithURL:(NSURL *)url settings:(NSDictionary<NSString *, id> *)settings error:(NSError **)outError;
  • url: 录制的语音文件保存的路径,文件的类型是由这个参数值的file
    extension推测的。
  • settings: 对audio recored的设置。在iOS7中,默认的设置是

{ AVFormatIDKey = 1819304813;  
AVLinearPCMBitDepthKey = 16;
AVLinearPCMIsBigEndianKey = 0;
AVLinearPCMIsFloatKey = 0; 
AVLinearPCMIsNonInterleaved = 0;
AVNumberOfChannelsKey = 2; 
AVSampleRateKey = 44100;}

下面的三个key适用于所有的语音格式:
AVFormatIDKey:格式的标识,它对应的是个枚举值。

CF_ENUM(AudioFormatID)
{
    kAudioFormatLinearPCM               = 'lpcm',
    kAudioFormatAC3                     = 'ac-3',
    kAudioFormat60958AC3                = 'cac3',
    kAudioFormatAppleIMA4               = 'ima4',
    kAudioFormatMPEG4AAC                = 'aac ',
    kAudioFormatMPEG4CELP               = 'celp',
    kAudioFormatMPEG4HVXC               = 'hvxc',
    kAudioFormatMPEG4TwinVQ             = 'twvq',
    kAudioFormatMACE3                   = 'MAC3',
    kAudioFormatMACE6                   = 'MAC6',
    kAudioFormatULaw                    = 'ulaw',
    kAudioFormatALaw                    = 'alaw',
    kAudioFormatQDesign                 = 'QDMC',
    kAudioFormatQDesign2                = 'QDM2',
    kAudioFormatQUALCOMM                = 'Qclp',
    kAudioFormatMPEGLayer1              = '.mp1',
    kAudioFormatMPEGLayer2              = '.mp2',
    kAudioFormatMPEGLayer3              = '.mp3',
    kAudioFormatTimeCode                = 'time',
    kAudioFormatMIDIStream              = 'midi',
    kAudioFormatParameterValueStream    = 'apvs',
    kAudioFormatAppleLossless           = 'alac',
    kAudioFormatMPEG4AAC_HE             = 'aach',
    kAudioFormatMPEG4AAC_LD             = 'aacl',
    kAudioFormatMPEG4AAC_ELD            = 'aace',
    kAudioFormatMPEG4AAC_ELD_SBR        = 'aacf',
    kAudioFormatMPEG4AAC_ELD_V2         = 'aacg',    
    kAudioFormatMPEG4AAC_HE_V2          = 'aacp',
    kAudioFormatMPEG4AAC_Spatial        = 'aacs',
    kAudioFormatAMR                     = 'samr',
    kAudioFormatAMR_WB                  = 'sawb',
    kAudioFormatAudible                 = 'AUDB',
    kAudioFormatiLBC                    = 'ilbc',
    kAudioFormatDVIIntelIMA             = 0x6D730011,
    kAudioFormatMicrosoftGSM            = 0x6D730031,
    kAudioFormatAES3                    = 'aes3',
    kAudioFormatEnhancedAC3             = 'ec-3'
};

AVSampleRateKey: 采样率,44.1kHZ和标准的CD
Audio是相同的,除非你需要一个高保真的录音,你不需要这样高的采样率,大部分的音频软件只能特定的速率像32KHZ,24KHZ,16KHZ,12KHZ.8KHZ是电话采样率,对一般的录音已经足够了。
AVNumberOfChannelsKey:通道数。设成2的话是双声道。iPhone只有一个麦克风,一个单声道的通道足够了,它把你的数据需求削减了一半。
参考

音频会话

在使用Apple设备时,我们注意到有些应用打开音频播放时,其他音频就会终止,而有些应用却可以同时使用音频,这就出现了多种声音的情况,这些音频环境就涉及了音频会话内容:iOS中,每个应用都有一个音频会话,及AVAudioSession,它属于AVFoundation框架中,是一种单例模式。

会话类型 说明 是否要求输入 是否需要输出 是否遵从静音键
AVAudioSessionCategoryAmbient 混音播放,可以与其他音频应用同时播放
AVAudioSessionCategorySoloAmbient 独占播放
AVAudioSessionCategoryPlayback 后台播放,也是独占的
AVAudioSessionCategoryRecord 录音模式,用于录音时使用
AVAudioSessionCategoryPlayAndRecord 播放和录音,此时可以录音也可以播放
AVAudioSessionCategoryAudioProcessing 硬件解码音频,此时不能播放和录制
AVAudioSessionCategoryMultiRoute 多种输入输出,例如可以耳机、USB设备同时播放

注意:是否遵循静音键表示在播放过程中如果用户通过硬件设置为静音是否能关闭声音

例:设置录音环境

NSError *sessionError;
// 设置音频会话为录音环境
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:&sessionError];
if (sessionError){
    NSLog(@"Error creating session: %@",[sessionError description]);
}else{
    // 启动该会话
    [[AVAudioSession sharedInstance] setActive:YES error:&sessionError];
}

说明:假如我们设置的音频会话为独占的话(如:后台播放),其他正在播放音频的应用就会被终止播放


音频的播放

音频的播放分为音效播放和音乐播放。前者是一些短音频播放,不需要进度、循环次数控制。后者是一些较长的音频,需要对进度、循环次数做精确控制。在iOS中,分别使用AudioToolbox.framework和AVFoundation.framework来完成音效和音频的播放

[TOC]

NSURL *url=[NSURL URLWithString:voiceDataPath];

iOS的AVFoundation框架中的AVAudioRecorder和AVAudioPlayer可以实现语音的录制和播放功能demo下载

参考地址

iOS开发系列–音频播放、录音、视频播放、拍照、视频录制

这样,就可以在C#
中发送消息给OC了,所有消息都可以通过这个接口来发送,只需要判断参数methodName来执行相应模块就可以了

[TOC]

2.iOS怎么调用原生的录音功能和播放功能

//停止播放

金沙js娱乐场官方网站 7

#import <AVFoundation/AVFoundation.h>

VoiceConvert
地址  

NSDictionary *setting=[NSMutableDictionary dictionary];

1.我们需要在AVAudioRecorder录制结束后,把wav格式的voiceData转化为arm格式然后发送给语音服务器

 

 

[setting setObject:@(8000) forKey:AVSampleRateKey];

我们知道iOS录制的格式只有wav的格式,这个格式明显回占用很大的内存空间,不方便发送数据到语音服务器或是下载,所以我们需要准换为压缩的音频数据格式易变减少录音文件的大小,保证语音聊天的流畅体验。