AVFoundation-01音頻播放與錄制

概述

AVFoundation 是一個可以用來使用和創(chuàng)建基于時間的視聽媒體數(shù)據(jù)的框架浑彰。AVFoundation 的構(gòu)建考慮到了目前的硬件環(huán)境和應(yīng)用程序蒙畴,其設(shè)計過程高度依賴多線程機(jī)制。充分利用了多核硬件的優(yōu)勢并大量使用block和GCD機(jī)制缀程,將復(fù)雜的計算機(jī)進(jìn)程放到了后臺線程運(yùn)行纪铺。會自動提供硬件加速操作,確保在大部分設(shè)備上應(yīng)用程序能以最佳性能運(yùn)行秩彤。該框架就是針對64位處理器設(shè)計的,可以發(fā)揮64位處理器的所有優(yōu)勢事哭。

iOS 媒體環(huán)境.png

數(shù)字媒體采樣

對媒體內(nèi)容進(jìn)行數(shù)字化主要有兩種方式呐舔。第一種稱為時間采樣,這種方法捕捉一個信號周期內(nèi)的變化慷蠕。第二種采樣方式是空間采樣,一般用在圖片數(shù)字化和其它可視媒體內(nèi)容數(shù)字化的過程食呻×骺唬空間采樣包含對一副圖片在一定分辨率之下捕捉其亮度和色度,進(jìn)而創(chuàng)建由該圖片的像素點(diǎn)數(shù)據(jù)所構(gòu)成的數(shù)字化結(jié)果仅胞。

音頻采樣

當(dāng)我們記錄一個聲音時每辟,一般會使用麥克風(fēng)設(shè)備。麥克風(fēng)設(shè)備是將機(jī)械能量(聲波)轉(zhuǎn)換成(電壓信號)的轉(zhuǎn)換設(shè)備干旧。目前在用的麥克風(fēng)種類很多渠欺,但是這里討論的麥克風(fēng)類型我們稱為電動式麥克風(fēng)。人類可以聽到的音頻范圍是20Hz~20KHz椎眯。

電動式麥克風(fēng)內(nèi)部圖.png

音頻數(shù)字化的過程包含一個編碼方法挠将,稱為線性脈沖編碼調(diào)制(linear pulse-code modulation),比較常見的說法是Linear PCM或LPCM胳岂。這個過程采樣或測量一個固定的音頻信號,過程的周期率被稱為采樣率舔稀。如果不斷提高采樣的頻率乳丰,我們就有可能以數(shù)字化方式準(zhǔn)確表現(xiàn)原始信號的信息。鑒于硬件條件我們還不能復(fù)制出完全一樣的效果内贮,但是我們能找打一個采樣率用于生成足夠好的數(shù)字呈現(xiàn)效果产园。我們稱其為奈奎斯特頻率(Nyquist rate).Harry Nyquist是貝爾實(shí)驗(yàn)室的一名工程師,他精確地捕捉到了一個特定頻率夜郁,該頻率為需要采樣對象的最高頻率的兩倍什燕。除采樣率外,數(shù)字音頻采樣的另一個重要方面是我們能夠捕捉到什么精度的音頻樣本竞端。振幅在線性坐標(biāo)系中進(jìn)行測量屎即,所以會有Linear PCM這個術(shù)語。用于保存樣本值的字節(jié)數(shù)定義了在線性維度上可行的離散度婶熬,同時這個信息也被稱為音頻的位元深度剑勾。為每個樣本的整體量化分配過少的位結(jié)果信息會導(dǎo)致數(shù)字音頻信號產(chǎn)生噪聲和扭曲。使用位元深度為8的方法可以提供256個離散級別數(shù)據(jù)赵颅。對于一些音頻資源來說虽另,這個級別的采樣率已經(jīng)足夠了,但對于大部分音頻內(nèi)容來說還不夠高饺谬。CD音質(zhì)的位元深度為16捂刺,可以達(dá)到65536個離散級別。專業(yè)級別的音頻錄制環(huán)境的位元深度可以達(dá)到24或更高募寨。

AVAudioPlayer

音頻播放器是很多應(yīng)用程序的需求族展,AVAudioPlayer 讓這一需求變得簡單,它提供了一種簡單地從文本或內(nèi)存中播放視頻的方法拔鹰。它提供了Audio Queue Services 中所能找到的核心功能仪缸。除非你需要從網(wǎng)絡(luò)流中播放音頻、需要訪問原始音頻樣本或者需要非常低的時延列肢,否則AVAudioPlayer都能勝任恰画。

  • 創(chuàng)建 AVAudioPlayer〈陕恚可以通過NSData和本地音頻文件的NSURL來創(chuàng)建拴还。
- (nullable instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;
- (nullable instancetype)initWithData:(NSData *)data error:(NSError **)outError;
  • 播放控制。如果返回一個有效的播放實(shí)例欧聘,可以調(diào)用 - (BOOL)prepareToPlay; 片林,這樣可以取得需要的音頻硬件并預(yù)加載Audio Queue的緩沖區(qū)。當(dāng)然,調(diào)用 - (void)play; 方法的時候會調(diào)用 - (BOOL)prepareToPlay;费封,如果優(yōu)先調(diào)用 - (BOOL)prepareToPlay; 方法焕妙,播放的時候再調(diào)用 - (void)play; 方法,可以降低一定的延時孝偎。
@property float pan; 
@property float volume;
@property float rate;
@property NSInteger numberOfLoops;

- (BOOL)prepareToPlay;
- (void)play;
- (BOOL)playAtTime:(NSTimeInterval)time;
- (void)pause;
- (void)stop;
  • 音頻計量访敌。默認(rèn)沒有開啟音頻計量,一旦啟用音頻測量可以通過 - (void)updateMeters; 方法更新測量值衣盾,我們通過 - (float)peakPowerForChannel:(NSUInteger)channelNumber; 寺旺、 - (float)averagePowerForChannel:(NSUInteger)channelNumber; 這兩個函數(shù)得到db,平均分貝势决,峰值分貝阻塑。
@property(getter=isMeteringEnabled) BOOL meteringEnabled; 

- (void)updateMeters; 
- (float)peakPowerForChannel:(NSUInteger)channelNumber; 
- (float)averagePowerForChannel:(NSUInteger)channelNumber; 
  • 靜音問題。當(dāng)手機(jī)在鈴聲果复、靜音間切換的時候陈莽,我們希望聲音不會停止播放,可以加下面的代碼虽抄。
NSError *error;
if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error]) {
    NSLog(@"setCategory error: %@", [error localizedDescription]);
    return;
}

if (![[AVAudioSession sharedInstance] setActive:YES error:&error]) {
    NSLog(@"setActive error: %@", [error localizedDescription]);
    return;
}
  • 鎖屏播放的問題走搁。在 Info.plist 文件中,加入以下配置迈窟。
<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
</array>
  • 中斷的問題私植。當(dāng)電話呼入、鬧鈴響起等车酣,在它們終止的時候曲稼,音頻是不會如預(yù)期的恢復(fù),我們可以監(jiān)聽相關(guān)通知來恢復(fù)湖员。
[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleInterruption:)
                                                     name:AVAudioSessionInterruptionNotification
                                                   object:nil];

- (void)handleInterruption:(NSNotification *)notice
{
    AVAudioSessionInterruptionType type = [notice.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (type == AVAudioSessionInterruptionTypeBegan) {
        [_musicPlayer pause];
    }else if (type == AVAudioSessionInterruptionTypeEnded) {
        [_musicPlayer play];
    }
}
  • 線路切換的問題贫悄。比如:在插上耳機(jī)時,我們希望聲音從耳機(jī)內(nèi)傳出娘摔,但我們拔掉耳機(jī)的時候窄坦,我們希望的是音樂停止播放。
[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleRouteChange:)
                                                     name:AVAudioSessionRouteChangeNotification
                                                   object:nil];

- (void)handleRouteChange:(NSNotification *)notice
{
    AVAudioSessionRouteChangeReason reason = [notice.userInfo[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
    if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
        AVAudioSessionRouteDescription *preRoute = notice.userInfo[AVAudioSessionRouteChangePreviousRouteKey];
        NSString *portType = [[preRoute.outputs firstObject] portType];
        if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
            [_musicPlayer pause];
        }
    }
}

AVAudioRecorder

AVAudioRecorder 也是構(gòu)建于 Audio Queue Services 之上凳寺,是一個功能強(qiáng)大且簡單易用的音頻錄制類嫡丙。

  • 創(chuàng)建 AVAudioRecorder。需要提供本文件的NSURL 以及 相關(guān)錄音參數(shù)設(shè)置读第。
- (nullable instancetype)initWithURL:(NSURL *)url settings:(NSDictionary<NSString *, id> *)settings error:(NSError **)outError;
- (nullable instancetype)initWithURL:(NSURL *)url format:(AVAudioFormat *)format error:(NSError **)outError;
  • 音頻格式。AVFormatIDKey 定義錄音文件的音頻格式拥刻。下面的常量都是設(shè)備所支持的值怜瞒。當(dāng)你指定的格式和URL的文件類型不一致的時候會出現(xiàn) The operation couldn’t be completed. (OSStatus error 1718449215.) 錯誤。
kAudioFormatLinearPCM
kAudioFormatMPEG4AAC
kAudioFormatAppleLossless
kAudioFormatAppleIMA4
kAudioFormatiLBC
kAudioFormatULaw
  • 采樣率。AVSampleRateKey 定義錄音文件的采樣率吴汪。一般來說采樣率越大惠窄,文件內(nèi)容也越大。對于使用什么樣的采樣率漾橙,我們可以盡量使用標(biāo)準(zhǔn)的采樣率杆融。如 8000Hz、16000Hz霜运、22050Hz脾歇、44100Hz。

  • 通道數(shù)淘捡。AVNumberOfChannelsKey 定義錄音文件的通道數(shù)藕各。一般使用默認(rèn)值1,即單聲道焦除。

NSDictionary *setting = @{
                           AVFormatIDKey : @(kAudioFormatMPEG4AAC),
                           AVSampleRateKey : @(44100),
                           AVNumberOfChannelsKey : @(1),
                           AVLinearPCMBitDepthKey : @(16),
                           AVEncoderAudioQualityKey : @(AVAudioQualityMedium)
                          };
  • 錄音控制激况。錄音的時候也可以先調(diào)用 - (BOOL)prepareToPlay; 真正錄制的時候再調(diào)用 - (void) record; 方法,可以降低一定的延時膘魄。
- (BOOL)prepareToRecord; 

- (BOOL)record;
- (BOOL)recordAtTime:(NSTimeInterval)time;
- (BOOL)recordForDuration:(NSTimeInterval) duration;
- (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration;

- (void)pause;
- (void)stop;

- (BOOL)deleteRecording;
  • 音頻計量乌逐。默認(rèn)沒有開啟音頻計量,一旦啟用音頻測量可以通過 - (void)updateMeters; 方法更新測量值创葡,我們通過 - (float)peakPowerForChannel:(NSUInteger)channelNumber;浙踢、 - (float)averagePowerForChannel:(NSUInteger)channelNumber; 這兩個函數(shù)得到db,平均分貝蹈丸,峰值分貝成黄。
@property(getter=isMeteringEnabled) BOOL meteringEnabled; 
- (void)updateMeters; 

- (float)peakPowerForChannel:(NSUInteger)channelNumber; 
- (float)averagePowerForChannel:(NSUInteger)channelNumber; 

音頻播放實(shí)例

1、新建 QMAudioController 播放控制類逻杖。

//
//  QMAudioController.m
//  AVFoundation
//
//  Created by mac on 17/6/20.
//  Copyright ? 2017年 Qinmin. All rights reserved.
//

#import "QMAudioController.h"
#import "QMMeterTable.h"

@interface QMAudioController ()
@property (nonatomic, strong) QMMeterTable *meterTable;
@end

@implementation QMAudioController

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (instancetype)initWithContentsOfURL:(NSURL *)url
{
    if (url && (self = [super init])) {
        NSError *error = nil;
        _musicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
        if (error) {
            NSLog(@"%@", [error localizedDescription]);
            return nil;
        }
       
        _musicPlayer.volume = 0.5f;
        _musicPlayer.pan = 0.0f;
        _musicPlayer.rate = 1.0f;
        _musicPlayer.numberOfLoops = -1;
        _musicPlayer.meteringEnabled = YES;
        [_musicPlayer prepareToPlay];
        
        _meterTable = [[QMMeterTable alloc] init];
        
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleInterruption:)
                                                     name:AVAudioSessionInterruptionNotification
                                                   object:nil];
        
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleRouteChange:)
                                                     name:AVAudioSessionRouteChangeNotification
                                                   object:nil];
        
        return self;
    }
    
    return nil;
}

- (void)play
{
    NSError *error;
    if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error]) {
        NSLog(@"setCategory error: %@", [error localizedDescription]);
        return;
    }

    if (![[AVAudioSession sharedInstance] setActive:YES error:&error]) {
        NSLog(@"setActive error: %@", [error localizedDescription]);
        return;
    }
    
    if (![_musicPlayer isPlaying]) {
        [_musicPlayer play];
    }
}

- (BOOL)playAtTime:(NSTimeInterval)time
{
    return [_musicPlayer playAtTime:time];
}

- (void)pause
{
    [_musicPlayer pause];
}

- (void)stop
{
    if ([_musicPlayer isPlaying]) {
        [_musicPlayer stop];
    }
}

- (BOOL)isPlaying
{
    return [_musicPlayer isPlaying];
}

- (void)updateMeters
{
    [_musicPlayer updateMeters];
}

- (float)peakValueForChannel:(NSUInteger)channelNumber
{
    return [_meterTable valueForPower:[_musicPlayer peakPowerForChannel:channelNumber]];
}

- (float)averageValueForChannel:(NSUInteger)channelNumber
{
    return [_meterTable valueForPower:[_musicPlayer averagePowerForChannel:channelNumber]];
}

#pragma mark - Notification
- (void)handleInterruption:(NSNotification *)notice
{
    AVAudioSessionInterruptionType type = [notice.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (type == AVAudioSessionInterruptionTypeBegan) {
        [_musicPlayer pause];
    }else if (type == AVAudioSessionInterruptionTypeEnded) {
        [_musicPlayer play];
    }
}

- (void)handleRouteChange:(NSNotification *)notice
{
    AVAudioSessionRouteChangeReason reason = [notice.userInfo[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
    if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
        AVAudioSessionRouteDescription *preRoute = notice.userInfo[AVAudioSessionRouteChangePreviousRouteKey];
        NSString *portType = [[preRoute.outputs firstObject] portType];
        if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
            [_musicPlayer pause];
        }
    }
}

@end

2奋岁、新建QMMeterTable音頻計量轉(zhuǎn)化類。我們將前面得到的分貝值的結(jié)果轉(zhuǎn)化我線性的0到1形式荸百,這需要一個轉(zhuǎn)化的方式闻伶,如果我們把一次計算的結(jié)果記錄下來,那么下次的計算就不需要了够话,因此提供下面的一個方法蓝翰。

//
//  QMMeterTable.m
//  AVFoundation
//
//  Created by mac on 17/6/21.
//  Copyright ? 2017年 Qinmin. All rights reserved.
//

#import "QMMeterTable.h"

#define MIN_DB          -60.0f
#define TABLE_SIZE      300


@interface QMMeterTable ()
{
    float                _scaleFactor;
    NSMutableArray      *_meterTable;
}
@end

@implementation QMMeterTable

static float dbToAmp(float dB)
{
    return powf(10.0f, 0.05f * dB);
}

- (id)init
{
    if (self = [super init]) {
        float dbResolution = MIN_DB / (TABLE_SIZE - 1);
        
        _meterTable = [NSMutableArray arrayWithCapacity:TABLE_SIZE];
        _scaleFactor = 1.0f / dbResolution;
        
        float minAmp = dbToAmp(MIN_DB);
        float ampRange = 1.0 - minAmp;
        float invAmpRange = 1.0 / ampRange;
        
        for (int i = 0; i < TABLE_SIZE; i++) {
            float decibels = i * dbResolution;
            float amp = dbToAmp(decibels);
            float adjAmp = (amp - minAmp) * invAmpRange;
            _meterTable[i] = @(adjAmp);
        }
    }
    return self;
}

- (float)valueForPower:(float)power
{
    if (power < MIN_DB) {
        return 0.0f;
    } else if (power >= 0.0f) {
        return 1.0f;
    } else {
        int index = (int) (power * _scaleFactor);
        return [_meterTable[index] floatValue];
    }
}
@end

3、使用播放控制類女嘲。每秒刷新12次去更新和獲取音頻計量畜份。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _slider.transform = CGAffineTransformMakeRotation(-M_PI_2);
    
    NSURL *musicURL = [[NSBundle mainBundle] URLForResource:@"1" withExtension:@"mp3"];
    _musicPlayer = [[QMAudioController alloc] initWithContentsOfURL:musicURL];
    
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateMeter:)];
    self.displayLink.frameInterval = 5;
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)updateMeter:(CADisplayLink *)link
{
        [_musicPlayer updateMeters];
        _slider.value = [_musicPlayer averageValueForChannel:0];
}

音頻錄制實(shí)例

1、新建 QMAudioRecorderController 音頻錄制管理類欣尼。

//
//  QMAudioRecorderController.m
//  AVFoundation
//
//  Created by mac on 17/6/21.
//  Copyright ? 2017年 Qinmin. All rights reserved.
//

#import "QMAudioRecorderController.h"
#import "QMMeterTable.h"

@interface QMAudioRecorderController()
@property (nonatomic, strong) QMMeterTable *meterTable;
@end

@implementation QMAudioRecorderController

- (instancetype)initWithContentsOfURL:(NSURL *)url
{
    if (url && (self = [super init])) {
        NSDictionary *setting = @{
                               AVFormatIDKey : @(kAudioFormatMPEG4AAC),
                               AVSampleRateKey : @(44100),
                               AVNumberOfChannelsKey : @(1),
                               AVLinearPCMBitDepthKey : @(16),
                               AVEncoderAudioQualityKey : @(AVAudioQualityMedium)
                               };
        
        NSError *error;
        _audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:setting error:&error];
        if (error) {
            NSLog(@"%@", [error localizedDescription]);
            return nil;
        }
        
        _meterTable = [[QMMeterTable alloc] init];
        _audioRecorder.meteringEnabled = YES;
        _audioRecorder.delegate = self;
        [_audioRecorder prepareToRecord];
        
        return self;
    }
    
    return nil;
}

- (BOOL)isRecording
{
    return [_audioRecorder isRecording];
}

- (BOOL)record
{
    NSError *error;
    if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]) {
        NSLog(@"setCategory error: %@", [error localizedDescription]);
        return NO;
    }
    
    if (![[AVAudioSession sharedInstance] setActive:YES error:&error]) {
        NSLog(@"setActive error: %@", [error localizedDescription]);
        return NO;
    }
    
    return [_audioRecorder record];
}

- (BOOL)recordAtTime:(NSTimeInterval)time
{
    return [_audioRecorder recordAtTime:time];
}

- (BOOL)recordForDuration:(NSTimeInterval) duration
{
    return [_audioRecorder recordForDuration:duration];
}

- (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration
{
    return [_audioRecorder recordAtTime:time forDuration:duration];
}

- (void)pause
{
    [_audioRecorder pause];
}

- (void)stop
{
    [_audioRecorder stop];
}

- (BOOL)deleteRecording
{
    return [_audioRecorder deleteRecording];
}

- (void)updateMeters
{
    [_audioRecorder updateMeters];
}

- (float)peakValueForChannel:(NSUInteger)channelNumber
{
    return [_meterTable valueForPower:[_audioRecorder peakPowerForChannel:channelNumber]];
}

- (float)averageValueForChannel:(NSUInteger)channelNumber
{
    return [_meterTable valueForPower:[_audioRecorder averagePowerForChannel:channelNumber]];
}

#pragma mark - AVAudioRecorderDelegate
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
    if (self.finishCallback) {
        self.finishCallback(flag, nil);
    }
}

- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error;
{
    NSLog(@"RecorderEncodeError error:%@", [error localizedDescription]);
    
    if (self.finishCallback) {
        self.finishCallback(NO, error);
    }
}

@end

2爆雹、新建QMMeterTable音頻計量轉(zhuǎn)化類停蕉。我們將前面得到的分貝值的結(jié)果轉(zhuǎn)化我線性的0到1形式,這需要一個轉(zhuǎn)化的方式钙态,如果我們把一次計算的結(jié)果記錄下來慧起,那么下次的計算就不需要了,因此提供下面的一個方法册倒。

//
//  QMMeterTable.m
//  AVFoundation
//
//  Created by mac on 17/6/21.
//  Copyright ? 2017年 Qinmin. All rights reserved.
//

#import "QMMeterTable.h"

#define MIN_DB          -60.0f
#define TABLE_SIZE      300


@interface QMMeterTable ()
{
    float                _scaleFactor;
    NSMutableArray      *_meterTable;
}
@end

@implementation QMMeterTable

static float dbToAmp(float dB)
{
    return powf(10.0f, 0.05f * dB);
}

- (id)init
{
    if (self = [super init]) {
        float dbResolution = MIN_DB / (TABLE_SIZE - 1);
        
        _meterTable = [NSMutableArray arrayWithCapacity:TABLE_SIZE];
        _scaleFactor = 1.0f / dbResolution;
        
        float minAmp = dbToAmp(MIN_DB);
        float ampRange = 1.0 - minAmp;
        float invAmpRange = 1.0 / ampRange;
        
        for (int i = 0; i < TABLE_SIZE; i++) {
            float decibels = i * dbResolution;
            float amp = dbToAmp(decibels);
            float adjAmp = (amp - minAmp) * invAmpRange;
            _meterTable[i] = @(adjAmp);
        }
    }
    return self;
}

- (float)valueForPower:(float)power
{
    if (power < MIN_DB) {
        return 0.0f;
    } else if (power >= 0.0f) {
        return 1.0f;
    } else {
        int index = (int) (power * _scaleFactor);
        return [_meterTable[index] floatValue];
    }
}

@end

3蚓挤、進(jìn)行音頻錄制。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _slider.transform = CGAffineTransformMakeRotation(-M_PI_2);
    
    NSURL *recordFileURL = [NSURL fileURLWithPath:kDocumentPath(@"1.aac")];
    _audioRecorder = [[QMAudioRecorderController alloc] initWithContentsOfURL:recordFileURL];
    
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateMeter:)];
    self.displayLink.frameInterval = 5;
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

#pragma mark - Timer
- (void)updateMeter:(CADisplayLink *)link
{
     [_audioRecorder updateMeters];
     _slider.value = [_audioRecorder averageValueForChannel:0];
}

參考

AVFoundation開發(fā)秘籍:實(shí)踐掌握iOS & OSX應(yīng)用的視聽處理技術(shù)

源碼地址:AVFoundation開發(fā) https://github.com/QinminiOS/AVFoundation

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驻子,一起剝皮案震驚了整個濱河市灿意,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拴孤,老刑警劉巖脾歧,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異演熟,居然都是意外死亡鞭执,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門芒粹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兄纺,“玉大人,你說我怎么就攤上這事化漆」来啵” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵座云,是天一觀的道長疙赠。 經(jīng)常有香客問我,道長朦拖,這世上最難降的妖魔是什么圃阳? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮璧帝,結(jié)果婚禮上捍岳,老公的妹妹穿的比我還像新娘。我一直安慰自己睬隶,他們只是感情好锣夹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著苏潜,像睡著了一般银萍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恤左,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天贴唇,我揣著相機(jī)與錄音贰锁,去河邊找鬼。 笑死滤蝠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的授嘀。 我是一名探鬼主播物咳,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蹄皱!你這毒婦竟也來了览闰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤巷折,失蹤者是張志新(化名)和其女友劉穎压鉴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锻拘,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡油吭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了署拟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婉宰。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖推穷,靈堂內(nèi)的尸體忽然破棺而出心包,到底是詐尸還是另有隱情,我是刑警寧澤馒铃,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布蟹腾,位于F島的核電站,受9級特大地震影響区宇,放射性物質(zhì)發(fā)生泄漏娃殖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一萧锉、第九天 我趴在偏房一處隱蔽的房頂上張望珊随。 院中可真熱鬧,春花似錦柿隙、人聲如沸叶洞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衩辟。三九已至,卻和暖如春波附,著一層夾襖步出監(jiān)牢的瞬間艺晴,已是汗流浹背昼钻。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留封寞,地道東北人然评。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像狈究,于是被迫代替她去往敵國和親碗淌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容