iOS音频开发相关(二)录音 `AVAudioRecorder`

[TOC]

录音 AVAudioRecorder

初始化方法

/* The file type to create can be set through the corresponding settings key. If not set, it will be inferred from the file extension. Will overwrite a file at the specified url if a file exists. */
- (nullable instancetype)initWithURL:(NSURL *)url settings:(NSDictionary<NSString *, id> *)settings error:(NSError **)outError;

// 录音文件的url
@property(readonly) NSURL *url; /* URL of the recorded file */

// 录音文件的设置信息
/* these settings are fully valid only when prepareToRecord has been called */
@property(readonly) NSDictionary<NSString *, id> *settings;
  • url: 录音文件保存的位置,一般都是保存到沙盒中

注意:
url一般使用[NSURL fileURLWithPath:_filePath]进行初始化,使用URLWithString:方法初始化会初始化失败;原因就是URL的格式,需要schemefileURLWithPath会添加file://,但是URLWithString不会做任何处理

  • settings: 录音的配置信息,取值如下:

例子:

设置:
    _settings = @{
                  AVFormatIDKey: @(kAudioFormatMPEG4AAC),
                  AVSampleRateKey: @(_sampleRate),
                  AVNumberOfChannelsKey: @2,
                  AVLinearPCMBitDepthKey: @16,
                  AVEncoderAudioQualityKey: @(AVAudioQualityHigh)
                  };
                  
// 打印结果:
_audioRecorder.settings: {
    AVAudioFileTypeKey = 1633973363;
    AVEncoderBitDepthHintKey = 0; // 默认的
    AVEncoderBitRateKey = 40000; // 默认的
    AVEncoderQualityKey = 96;
    AVFormatIDKey = 1633772320;
    AVNumberOfChannelsKey = 2;
    AVSampleRateKey = 11025;
}

注意,对于录音音量比较小的问题,需要添加下面的代码

 // 音量控制,否则声音很小
    NSError *volumError = nil;
    [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&volumError];
    if (volumError) {
        NSLog(@"音量控制失败 --- ");
        NSLog(@"%@", volumError);
    }

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',
    kAudioFormatFLAC                    = 'flac',
    kAudioFormatOpus                    = 'opus'
};
  • kAudioFormatLinearPCM: 音频的原始文件(caf
  • kAudioFormatMPEG4AAC: aac文件格式的音频

注意
文件格式不可以直接.mp3格式,会报错

AVSampleRateKey

采用率(赫兹)``

常用的有

  • 8,000 Hz: 电话所用采样率, 对于人的说话已经足够
  • 11,025 Hz: 说话

参考文章:

https://baike.baidu.com/item/%E9%9F%B3%E9%A2%91%E9%87%87%E6%A0%B7%E7%8E%87/9023551

AVNumberOfChannelsKey

采用通道数,一般有:

  • 1: 单声道
  • 2: 双声道

linear PCM keys

AVLinearPCMBitDepthKey

采样深度,影响声音质量,比如从cafmp3,如果设置为8的话,声音可能会失真,需要设置更高一点(比如16)

取值有: 8, 16, 24, 32

AVLinearPCMIsBigEndianKey

  • YES: 大端模式
  • NO: 小端模式

参考文章:

https://blog.csdn.net/chivalrousli/article/details/38419995

AVLinearPCMIsFloatKey

是否支持浮点处理

AVLinearPCMIsNonInterleaved

交叉的

audio file type key 11.0 之后

AVAudioFileTypeKey

AudioFile.h文件中可以查看具体的

encoder property keys

编码属性

AVEncoderAudioQualityKey

编码质量

取值有:

typedef NS_ENUM(NSInteger, AVAudioQuality) {
    AVAudioQualityMin    = 0,
    AVAudioQualityLow    = 0x20,
    AVAudioQualityMedium = 0x40,
    AVAudioQualityHigh   = 0x60,
    AVAudioQualityMax    = 0x7F
};

AVEncoderAudioQualityForVBRKey

动态比特率,取值是 AVAudioQuality,只和AVAudioBitRateStrategy_Variable有关

AVEncoderBitRateKey

编码比特率

注意
AVEncoderBitRateKeyAVEncoderBitRatePerChannelKey只需要设置一个 ;only one of AVEncoderBitRateKey and AVEncoderBitRatePerChannelKey should be provided.

AVEncoderBitRatePerChannelKey

每个声道的比特率

AVEncoderBitRateStrategyKey

编码比特率的策略

取值有:

/* values for AVEncoderBitRateStrategyKey */
AVF_EXPORT NSString *const AVAudioBitRateStrategy_Constant              NS_AVAILABLE(10_9, 7_0);
AVF_EXPORT NSString *const AVAudioBitRateStrategy_LongTermAverage       NS_AVAILABLE(10_9, 7_0);
AVF_EXPORT NSString *const AVAudioBitRateStrategy_VariableConstrained   NS_AVAILABLE(10_9, 7_0);
AVF_EXPORT NSString *const AVAudioBitRateStrategy_Variable              NS_AVAILABLE(10_9, 7_0);

AVEncoderBitDepthHintKey

取值 8 to 32

控制方法

// 准备录音
- (BOOL)prepareToRecord; /* creates the file and gets ready to record. happens automatically on record. */

// 开始录音
- (BOOL)record; /* start or resume recording to file. */

// 在设定的时间time开始录音
- (BOOL)recordAtTime:(NSTimeInterval)time NS_AVAILABLE_IOS(6_0); /* start recording at specified time in the future. time is an absolute time based on and greater than deviceCurrentTime. */

// 录音时间为 duration 秒,超过会自动停止
- (BOOL)recordForDuration:(NSTimeInterval) duration; /* record a file of a specified duration. the recorder will stop when it has recorded this length of audio */

// 在设定的时间time开始录音,录音最大时间长为 duration
- (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration NS_AVAILABLE_IOS(6_0); /* record a file of a specified duration starting at specified time. time is an absolute time based on and greater than deviceCurrentTime. */

// 暂停
- (void)pause; /* pause recording */

// 停止
- (void)stop; /* stops recording. closes the file. */

// 删除录音
- (BOOL)deleteRecording; /* delete the recorded file. recorder must be stopped. returns NO on failure. */

// 是否正在录音中
@property(readonly, getter=isRecording) BOOL recording; /* is it recording or not? */

代理

/* the delegate will be sent messages from the AVAudioRecorderDelegate protocol */ 
@property(assign, nullable) id<AVAudioRecorderDelegate> delegate;
@protocol AVAudioRecorderDelegate <NSObject>
@optional 

/* audioRecorderDidFinishRecording:successfully: is called when a recording has been finished or stopped. This method is NOT called if the recorder is stopped due to an interruption. */
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag;

/* if an error occurs while encoding it will be reported to the delegate. */
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error;

#if TARGET_OS_IPHONE

/* AVAudioRecorder INTERRUPTION NOTIFICATIONS ARE DEPRECATED - Use AVAudioSession instead. */

/* audioRecorderBeginInterruption: is called when the audio session has been interrupted while the recorder was recording. The recorded file will be closed. */
- (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder NS_DEPRECATED_IOS(2_2, 8_0);

/* audioRecorderEndInterruption:withOptions: is called when the audio session interruption has ended and this recorder had been interrupted while recording. */
/* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder withOptions:(NSUInteger)flags NS_DEPRECATED_IOS(6_0, 8_0);

- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder withFlags:(NSUInteger)flags NS_DEPRECATED_IOS(4_0, 6_0);

/* audioRecorderEndInterruption: is called when the preferred method, audioRecorderEndInterruption:withFlags:, is not implemented. */
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder NS_DEPRECATED_IOS(2_2, 6_0);

#endif // TARGET_OS_IPHONE

@end

录音时间

/* get the current time of the recording - only valid while recording */
@property(readonly) NSTimeInterval currentTime;
/* get the device current time - always valid */
@property(readonly) NSTimeInterval deviceCurrentTime NS_AVAILABLE_IOS(6_0);
  • currentTime: 当前的录音时间,录音时有效;停止录音的时候为0
  • deviceCurrentTime: 当前的设备时间

录音时间的处理:

  • record时记录deviceCurrentTime_recorderBeginTime
  • 录音完成的代理方法audioRecorderDidFinishRecording记录结束时间为_recorderEndTime
  • _recorderEndTime减去_recorderBeginTime就得到了录音时间长,不过这只是一个大概的,不是很准确,aac格式会有误差;

注意
1、使用caf格式录音时,时间是准确的,设置recordForDuration:方法也是有效的、准确的;
2、使用aac格式录音时,设置recordForDuration:方法也是准确的,有误差1秒多

录音最大时间的处理:

  • 1、设置最大时间的方法:
BOOL result = [_audioRecorder recordAtTime:_audioRecorder.deviceCurrentTime forDuration:_maxDuration]
  • 2、开启定时器进行处理

注:在定时器方法中也需要处理最大时间,aac文件格式录音时间是不准确的

- (void)timerFire {
    // 主线程执行
    dispatch_async(dispatch_get_main_queue(), ^{
        BOOL result = _audioRecorder.isRecording;
        NSTimeInterval time = result ? _audioRecorder.currentTime : _audioPlayer.currentTime;
        NSLog(@"currentTime: %f", _audioRecorder.currentTime);
        NSLog(@"deviceCurrentTime: %f", _audioRecorder.deviceCurrentTime);
        NSLog(@"_recorderBeginTime: %f", _recorderBeginTime);
        NSLog(@"duration: %f", _audioRecorder.deviceCurrentTime - _recorderBeginTime);
        // 录音的最大时间处理: (aac格式时间不准确,caf准确)
        if (result) {
            if (time > _maxDuration) {
                [self stop];
                return;
            }
        }
        [self notifyToDelegateDidRecordingOrPalying:result currentTime:time];
    });
}

补充

/* get the current time of the recording - only valid while recording */
@property(readonly) NSTimeInterval currentTime;
/* get the device current time - always valid */
@property(readonly) NSTimeInterval deviceCurrentTime NS_AVAILABLE_IOS(6_0);
  • currentTime: 当前时间,录音时有效,停止录音的时候为0
  • deviceCurrentTime: 一直有效,有值

代码:

NSLog(@"_audioRecorder 初始化完成");
[self _logRecordTime];
    
/// ------
    
- (BOOL)record {
   NSLog(@"_audioRecorder 准备");
   [self _logRecordTime];

    BOOL result = [_audioRecorder recordAtTime:_audioRecorder.deviceCurrentTime forDuration:_maxDuration];
   if (result) {
       NSLog(@"_audioRecorder 开始");
       [self _logRecordTime];
       _recorderBeginTime = _audioRecorder.deviceCurrentTime;
   } else {
       [self notifyToDelegateRecorderInitFailed];
   }
   return result;
}
2018-08-22 18:06:38.177723+0800 HaiZiGuoParents[42414:12224854] _audioRecorder 初始化完成
2018-08-22 18:06:38.177745+0800 HaiZiGuoParents[42414:12224854] _audioRecorder.deviceCurrentTime: 1388447.325438
2018-08-22 18:06:38.177769+0800 HaiZiGuoParents[42414:12224854] _audioRecorder.currentTime: 0.000000
2018-08-22 18:06:38.177798+0800 HaiZiGuoParents[42414:12224854] _audioRecorder 准备
2018-08-22 18:06:38.177950+0800 HaiZiGuoParents[42414:12224854] _audioRecorder.deviceCurrentTime: 1388447.325647
2018-08-22 18:06:38.177971+0800 HaiZiGuoParents[42414:12224854] _audioRecorder.currentTime: 0.000000
2018-08-22 18:06:38.394655+0800 HaiZiGuoParents[42414:12224854] _audioRecorder 开始
2018-08-22 18:06:38.394743+0800 HaiZiGuoParents[42414:12224854] _audioRecorder.deviceCurrentTime: 1388447.542438
2018-08-22 18:06:38.394961+0800 HaiZiGuoParents[42414:12224854] _audioRecorder.currentTime: 0.026122

1388447.542438 - 1388447.325647 = 0.216791 可以看到以录音deviceCurrentTime作为标准计算时间为 0.216791,实际上currentTime0.026122,差了一些

分贝

// 是否开启分贝检测
@property(getter=isMeteringEnabled) BOOL meteringEnabled; /* turns level metering on or off. default is off. */

// 更新,在调用下面👇两个方法之前需要先调用这个方法
- (void)updateMeters; /* call to refresh meter values */

// 当前分贝
- (float)peakPowerForChannel:(NSUInteger)channelNumber; /* returns peak power in decibels for a given channel */

// 平均分贝
- (float)averagePowerForChannel:(NSUInteger)channelNumber; /* returns average power in decibels for a given channel */

备注

采样率:

  • 11025.0: 5分钟1.5M

推荐阅读更多精彩内容