You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
12 KiB
323 lines
12 KiB
![]()
2 years ago
|
//
|
||
|
// XHVoiceRecordHelper.m
|
||
|
// MessageDisplayExample
|
||
|
//
|
||
|
// Created by HUAJIE-1 on 14-5-13.
|
||
|
// Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved.
|
||
|
//
|
||
|
#import <UIKit/UIKit.h>
|
||
|
|
||
|
#import "XHVoiceRecordHelper.h"
|
||
|
#import "XHMacro.h"
|
||
|
|
||
|
#import "SVProgressHUD.h"
|
||
|
|
||
|
#import "JLAuthorizationManager.h"
|
||
|
#import "myHelper.h"
|
||
|
|
||
|
@interface XHVoiceRecordHelper () <AVAudioRecorderDelegate> {
|
||
|
NSTimer *_timer;
|
||
|
|
||
|
BOOL _isPause;
|
||
|
|
||
|
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||
|
UIBackgroundTaskIdentifier _backgroundIdentifier;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
@property (nonatomic, copy, readwrite) NSString *recordPath;
|
||
|
@property (nonatomic, readwrite) NSTimeInterval currentTimeInterval;
|
||
|
|
||
|
|
||
|
@end
|
||
|
|
||
|
@implementation XHVoiceRecordHelper
|
||
|
|
||
|
- (id)init {
|
||
|
self = [super init];
|
||
|
if (self) {
|
||
|
self.maxRecordTime = kVoiceRecorderTotalTime;
|
||
|
self.recordDuration = @(0);
|
||
|
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||
|
_backgroundIdentifier = UIBackgroundTaskInvalid;
|
||
|
#endif
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (void)dealloc {
|
||
|
[self stopRecord];
|
||
|
self.recordPath = nil;
|
||
|
[self stopBackgroundTask];
|
||
|
}
|
||
|
|
||
|
- (void)startBackgroundTask {
|
||
|
[self stopBackgroundTask];
|
||
|
|
||
|
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||
|
_backgroundIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
||
|
[self stopBackgroundTask];
|
||
|
}];
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
- (void)stopBackgroundTask {
|
||
|
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||
|
if (_backgroundIdentifier != UIBackgroundTaskInvalid) {
|
||
|
[[UIApplication sharedApplication] endBackgroundTask:_backgroundIdentifier];
|
||
|
_backgroundIdentifier = UIBackgroundTaskInvalid;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
- (void)resetTimer {
|
||
|
if (!_timer)
|
||
|
return;
|
||
|
|
||
|
if (_timer) {
|
||
|
[_timer invalidate];
|
||
|
_timer = nil;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
- (void)cancelRecording {
|
||
|
if (!_recorder)
|
||
|
return;
|
||
|
|
||
|
if (self.recorder.isRecording) {
|
||
|
[self.recorder stop];
|
||
|
}
|
||
|
|
||
|
self.recorder = nil;
|
||
|
}
|
||
|
|
||
|
- (void)stopRecord {
|
||
|
[self cancelRecording];
|
||
|
[self resetTimer];
|
||
|
}
|
||
|
|
||
|
- (BOOL)canRecord
|
||
|
{
|
||
|
__block BOOL bCanRecord = NO;
|
||
|
JLAuthorizationManager *jlManager = [JLAuthorizationManager defaultManager];
|
||
|
[jlManager JL_requestAuthorizationWithAuthorizationType:JLAuthorizationTypeAudio
|
||
|
authorizedHandler:^{
|
||
|
bCanRecord = YES;
|
||
|
}
|
||
|
unAuthorizedHandler:^{}];
|
||
|
|
||
|
return bCanRecord;
|
||
|
}
|
||
|
|
||
|
- (void)prepareRecordingWithPath:(NSString *)path prepareRecorderCompletion:(XHPrepareRecorderCompletion)prepareRecorderCompletion {
|
||
|
|
||
|
WEAKSELF
|
||
|
JLAuthorizationManager *jlManager = [JLAuthorizationManager defaultManager];
|
||
|
[jlManager JL_requestAuthorizationWithAuthorizationType:JLAuthorizationTypeAudio
|
||
|
authorizedHandler:nil
|
||
|
unAuthorizedHandler:^{
|
||
|
LGAlertView *alertView = [[LGAlertView alloc] initWithTitle:[NSString stringWithFormat:@"请为%@打开麦克风权限",APPName] message:nil style:LGAlertViewStyleAlert buttonTitles:@[@"确定"] cancelButtonTitle:@"取消" destructiveButtonTitle:nil actionHandler:^(LGAlertView *alertView, NSString *title, NSUInteger index) {
|
||
|
|
||
|
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||
|
|
||
|
} cancelHandler:^(LGAlertView *alertView) {
|
||
|
|
||
|
} destructiveHandler:nil];
|
||
|
[alertView showAnimated:YES completionHandler:nil];
|
||
|
}];
|
||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||
|
_isPause = NO;
|
||
|
|
||
|
NSError *error = nil;
|
||
|
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||
|
[audioSession setCategory :AVAudioSessionCategoryPlayAndRecord error:&error];
|
||
|
if(error) {
|
||
|
DLog(@"audioSession: %@ %ld %@", [error domain], (long)[error code], [[error userInfo] description]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
error = nil;
|
||
|
[audioSession setActive:YES error:&error];
|
||
|
if(error) {
|
||
|
DLog(@"audioSession: %@ %ld %@", [error domain], (long)[error code], [[error userInfo] description]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//MARK: 设置录音设置
|
||
|
NSDictionary * recordSetting = @{
|
||
|
AVSampleRateKey:[NSNumber numberWithFloat:8000.0], //采样率
|
||
|
AVFormatIDKey:[NSNumber numberWithInt:kAudioFormatLinearPCM],
|
||
|
AVLinearPCMBitDepthKey:[NSNumber numberWithInt:16],//采样位数 默认 16
|
||
|
AVNumberOfChannelsKey:[NSNumber numberWithInt:2],//通道的数目
|
||
|
AVEncoderBitRateKey:[NSNumber numberWithFloat:128000],
|
||
|
// [recordSetting setValue :[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
|
||
|
// [recordSetting setValue:[NSNumber numberWithFloat:16000.0] forKey:AVSampleRateKey];
|
||
|
// [recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];
|
||
|
// AVEncoderAudioQualityKey: [NSNumber numberWithInt: AVAudioQualityMax],//音频编码质量
|
||
|
AVEncoderAudioQualityKey: [NSNumber numberWithInt: AVAudioQualityHigh],//音频编码质量
|
||
|
};
|
||
|
|
||
|
// MARK: 乐家守护录音设置参数
|
||
|
NSDictionary *recordSetting2 = [[NSDictionary alloc] initWithObjectsAndKeys:
|
||
|
[NSNumber numberWithFloat: 8000.0],AVSampleRateKey, //采样率
|
||
|
[NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,
|
||
|
[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,//采样位数 默认 16
|
||
|
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,//通道的数目
|
||
|
// [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,//大端还是小端 是内存的组织方式
|
||
|
// [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,//采样信号是整数还是浮点数
|
||
|
// [NSNumber numberWithInt: AVAudioQualityMedium],AVEncoderAudioQualityKey,//音频编码质量
|
||
|
nil];
|
||
|
|
||
|
if (weakSelf) {
|
||
|
STRONGSELF
|
||
|
strongSelf.recordPath = path;
|
||
|
error = nil;
|
||
|
|
||
|
if (strongSelf.recorder) {
|
||
|
[strongSelf cancelRecording];
|
||
|
} else {
|
||
|
strongSelf.recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:strongSelf.recordPath] settings:recordSetting2 error:&error];
|
||
|
strongSelf.recorder.delegate = strongSelf;
|
||
|
[strongSelf.recorder prepareToRecord];
|
||
|
strongSelf.recorder.meteringEnabled = YES;
|
||
|
[strongSelf.recorder recordForDuration:(NSTimeInterval) 160];
|
||
|
[strongSelf startBackgroundTask];
|
||
|
}
|
||
|
|
||
|
if(error) {
|
||
|
DLog(@"audioSession: %@ %ld %@", [error domain], (long)[error code], [[error userInfo] description]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
|
||
|
//上層如果傳會來說已經取消了, 那這邊就做原先取消的動作
|
||
|
if (!prepareRecorderCompletion()) {
|
||
|
[strongSelf cancelledDeleteWithCompletion:^{
|
||
|
}];
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
- (void)startRecordingWithStartRecorderCompletion:(XHStartRecorderCompletion)startRecorderCompletion {
|
||
|
if ([_recorder record]) {
|
||
|
[self resetTimer];
|
||
|
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateMeters) userInfo:nil repeats:YES];
|
||
|
if (startRecorderCompletion)
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
startRecorderCompletion();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)resumeRecordingWithResumeRecorderCompletion:(XHResumeRecorderCompletion)resumeRecorderCompletion {
|
||
|
_isPause = NO;
|
||
|
if (_recorder) {
|
||
|
if ([_recorder record]) {
|
||
|
dispatch_async(dispatch_get_main_queue(), resumeRecorderCompletion);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)pauseRecordingWithPauseRecorderCompletion:(XHPauseRecorderCompletion)pauseRecorderCompletion {
|
||
|
_isPause = YES;
|
||
|
if (_recorder) {
|
||
|
[_recorder pause];
|
||
|
}
|
||
|
if (!_recorder.isRecording)
|
||
|
dispatch_async(dispatch_get_main_queue(), pauseRecorderCompletion);
|
||
|
}
|
||
|
|
||
|
- (void)stopRecordingWithStopRecorderCompletion:(XHStopRecorderCompletion)stopRecorderCompletion {
|
||
|
_isPause = NO;
|
||
|
[self stopBackgroundTask];
|
||
|
[self stopRecord];
|
||
|
[self getVoiceDuration:_recordPath];
|
||
|
dispatch_async(dispatch_get_main_queue(), stopRecorderCompletion);
|
||
|
}
|
||
|
|
||
|
- (void)cancelledDeleteWithCompletion:(XHCancellRecorderDeleteFileCompletion)cancelledDeleteCompletion {
|
||
|
|
||
|
_isPause = NO;
|
||
|
[self stopBackgroundTask];
|
||
|
[self stopRecord];
|
||
|
|
||
|
if (self.recordPath) {
|
||
|
// 删除目录下的文件
|
||
|
NSFileManager *fileManeger = [NSFileManager defaultManager];
|
||
|
if ([fileManeger fileExistsAtPath:self.recordPath]) {
|
||
|
NSError *error = nil;
|
||
|
[fileManeger removeItemAtPath:self.recordPath error:&error];
|
||
|
if (error) {
|
||
|
DLog(@"error :%@", error.description);
|
||
|
}
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
cancelledDeleteCompletion(error);
|
||
|
});
|
||
|
} else {
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
cancelledDeleteCompletion(nil);
|
||
|
});
|
||
|
}
|
||
|
} else {
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
cancelledDeleteCompletion(nil);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)updateMeters {
|
||
|
if (!_recorder)
|
||
|
return;
|
||
|
|
||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||
|
[_recorder updateMeters];
|
||
|
|
||
|
self.currentTimeInterval = _recorder.currentTime;
|
||
|
|
||
|
if (!_isPause) {
|
||
|
float progress = self.currentTimeInterval / self.maxRecordTime * 1.0;
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
if (_recordProgress) {
|
||
|
_recordProgress(progress);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
float peakPower = [_recorder averagePowerForChannel:0];
|
||
|
double ALPHA = 0.015;
|
||
|
double peakPowerForChannel = pow(10, (ALPHA * peakPower));
|
||
|
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
// 更新扬声器
|
||
|
if (_peakPowerForChannel) {
|
||
|
_peakPowerForChannel(peakPowerForChannel);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
if (self.currentTimeInterval > self.maxRecordTime) {
|
||
|
[self stopRecord];
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
_maxTimeStopRecorderCompletion();
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
- (void)getVoiceDuration:(NSString*)recordPath {
|
||
|
NSError *error = nil;
|
||
|
AVAudioPlayer *play = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:recordPath] error:&error];
|
||
|
if (error) {
|
||
|
DLog(@"recordPath:%@ error:%@", recordPath, error);
|
||
|
self.recordDuration = @(0);
|
||
|
} else {
|
||
|
DLog(@"时长:%f", play.duration);
|
||
|
self.recordDuration = @(play.duration);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@end
|