Files
featherVoice/QXLive/Mine(音域)/View/歌手认证/QXAudioRecorderView.m

747 lines
27 KiB
Mathematica
Raw Normal View History

2025-11-21 16:17:05 +08:00
//
// QXAudioRecorderView.m
// QXLive
//
// Created by on 2025/11/13.
//
#import "QXAudioRecorderView.h"
#import <AVFoundation/AVFoundation.h>
#import "UIButton+QX.h"
#import "QXCOSUploadManager.h"
#import "QXMineNetwork.h"
@interface QXAudioRecorderView() <AVAudioRecorderDelegate, AVAudioPlayerDelegate,UIGestureRecognizerDelegate>
// UI Components
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIView *recordContentView;
@property (nonatomic, strong) UIImageView *recordButton;
@property (nonatomic, strong) CAShapeLayer *progressLayer;
@property (nonatomic, strong) UILabel *timeLabel;
@property (nonatomic, strong) UILabel *stateLabel;
@property (nonatomic, strong) UIButton *playButton;
@property (nonatomic, strong) UIButton *resetButton;
@property (nonatomic, strong) UIButton *authBtn;
// Audio Components
@property (nonatomic, strong) AVAudioRecorder *audioRecorder;
@property (nonatomic, strong) AVAudioPlayer *audioPlayer;
@property (nonatomic, strong) NSURL *originalAudioFileURL;
@property (nonatomic, strong) NSURL *mp3AudioFileURL;
@property (nonatomic, strong) NSURL *tempAudioFileURL; //
// Recording State
@property (nonatomic, assign) AudioRecorderState state;
@property (nonatomic, assign) NSTimeInterval currentDuration; //
@property (nonatomic, assign) NSTimeInterval totalDuration; //
@property (nonatomic, assign) NSInteger recordingSessionCount; //
@property (nonatomic, strong) NSTimer *recordingTimer;
@property (nonatomic, assign) BOOL isCancelled;
@property (nonatomic, strong) NSMutableArray<NSURL *> *recordedSegments; //
@end
@implementation QXAudioRecorderView
- (instancetype)initWithFrame:(CGRect)frame {
return [self initWithFrame:frame maxDuration:300.0];
}
- (instancetype)initWithFrame:(CGRect)frame maxDuration:(NSTimeInterval)maxDuration {
self = [super initWithFrame:frame];
if (self) {
_maxDuration = maxDuration;
_showPlaybackButton = YES;
_state = AudioRecorderStateReady;
_recordedSegments = [NSMutableArray array];
_recordingSessionCount = 0;
[self setupUI];
[self setupAudioSession];
[self setupGestureRecognizers];
}
return self;
}
- (void)setupUI {
self.backgroundColor = [UIColor whiteColor];
[self addRoundedCornersWithRadius:16 byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)];
self.authBtn = [[UIButton alloc] initWithFrame:CGRectMake(SCREEN_WIDTH-ScaleWidth(57)-16, 16, ScaleWidth(57), ScaleWidth(20))];
[self.authBtn setImage:[UIImage imageNamed:@"singer_auth_btn"] forState:(UIControlStateNormal)];
[self.authBtn addTarget:self action:@selector(authAction) forControlEvents:(UIControlEventTouchUpInside)];
[self addSubview:self.authBtn];
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake((SCREEN_WIDTH-150)/2, 18, 150, 24)];
self.titleLabel.textColor = RGB16(0x333333);
self.titleLabel.font = [UIFont boldSystemFontOfSize:16];
self.titleLabel.text = @"我的认证";
self.titleLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.titleLabel];
self.recordContentView = [[UIView alloc] init];
[self addSubview:self.recordContentView];
//
self.recordButton = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"singer_record_btn"]];
self.recordButton.userInteractionEnabled = YES;
[self.recordContentView addSubview:self.recordButton];
//
self.progressLayer = [CAShapeLayer layer];
self.progressLayer.fillColor = [UIColor clearColor].CGColor;
self.progressLayer.strokeColor = QXConfig.themeColor.CGColor;
self.progressLayer.lineWidth = 4;
self.progressLayer.strokeEnd = 0;
//
self.progressLayer.strokeStart = 0; //
self.progressLayer.strokeEnd = 0; // 0
// transform
self.progressLayer.transform = CATransform3DIdentity;
self.progressLayer.lineCap = kCALineCapRound;
[self.recordContentView.layer addSublayer:self.progressLayer];
//
self.timeLabel = [[UILabel alloc] init];
self.timeLabel.text = [self formattedTime:0];
self.timeLabel.textAlignment = NSTextAlignmentCenter;
self.timeLabel.font = [UIFont systemFontOfSize:12];
self.timeLabel.textColor = RGB16A(0x000000, 0.45);
[self addSubview:self.timeLabel];
//
self.stateLabel = [[UILabel alloc] init];
self.stateLabel.text = @"长按录音";
self.stateLabel.textAlignment = NSTextAlignmentCenter;
self.stateLabel.font = [UIFont systemFontOfSize:14];
self.stateLabel.textColor = [UIColor grayColor];
[self addSubview:self.stateLabel];
//
// //
// self.sessionLabel = [[UILabel alloc] init];
// self.sessionLabel.text = @"";
// self.sessionLabel.textAlignment = NSTextAlignmentCenter;
// self.sessionLabel.font = [UIFont systemFontOfSize:12];
// self.sessionLabel.textColor = [UIColor lightGrayColor];
// [self addSubview:self.sessionLabel];
//
self.playButton = [[UIButton alloc] init];
[self.playButton setTitle:@"试听" forState:UIControlStateNormal];
[self.playButton setTitleColor:RGB16A(0x000000, 0.45) forState:(UIControlStateNormal)];
self.playButton.titleLabel.font = [UIFont systemFontOfSize:12];
[self.playButton setImage:[UIImage imageNamed:@"singer_try_listen"] forState:(UIControlStateNormal)];
[self.playButton addTarget:self action:@selector(playButtonTapped) forControlEvents:UIControlEventTouchUpInside];
// self.playButton.hidden = YES;
[self addSubview:self.playButton];
//
self.resetButton = [[UIButton alloc] init];
[self.resetButton setTitle:@"重置" forState:UIControlStateNormal];
[self.resetButton setImage:[UIImage imageNamed:@"singer_reset_record"] forState:(UIControlStateNormal)];
[self.resetButton setTitleColor:RGB16A(0x000000, 0.45) forState:(UIControlStateNormal)];
self.resetButton.titleLabel.font = [UIFont systemFontOfSize:12];
[self.resetButton addTarget:self action:@selector(resetButtonTapped) forControlEvents:UIControlEventTouchUpInside];
// self.resetButton.hidden = YES;
[self addSubview:self.resetButton];
[self.recordContentView bringSubviewToFront:self.recordButton];
}
- (void)setupAudioSession {
// AVAudioSession *audioSession = [AVAudioSession sharedInstance];
// NSError *error = nil;
// [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
// [audioSession setActive:YES error:&error];
//
// if (error) {
// NSLog(@"Audio session setup error: %@", error);
// }
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
// 使 Playback 使
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
// 使 PlayAndRecord
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker
error:&error];
[audioSession setActive:YES error:&error];
if (error) {
NSLog(@"Audio session setup error: %@", error);
}
}
- (void)setupGestureRecognizers {
// -
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
longPress.minimumPressDuration = 0.3;
longPress.delegate = self;
[self.recordButton addGestureRecognizer:longPress];
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat centerX = CGRectGetWidth(self.bounds) / 2;
//
self.timeLabel.frame = CGRectMake((SCREEN_WIDTH-150)/2, self.titleLabel.bottom+4, 150, 30);
//
self.recordContentView.frame = CGRectMake(centerX - 50.5, self.timeLabel.bottom+22, 101, 101);
self.recordButton.frame = CGRectMake(11, 11, 80, 80);
//
CGRect progressRect = CGRectInset(self.recordContentView.bounds, 4, 4);
UIBezierPath *progressPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(progressRect), CGRectGetMidY(progressRect))
radius:progressRect.size.width / 2
startAngle:-M_PI_2 // -90
endAngle:-M_PI_2 + M_PI * 2 //
clockwise:YES];
self.progressLayer.path = progressPath.CGPath;
// //
self.stateLabel.frame = CGRectMake(0, self.recordContentView.bottom, CGRectGetWidth(self.bounds), 20);
//
// //
// self.sessionLabel.frame = CGRectMake(0, 205, CGRectGetWidth(self.bounds), 20);
//
self.playButton.frame = CGRectMake(SCREEN_WIDTH-38-35, self.recordContentView.top+35, 38, 60);
self.resetButton.frame = CGRectMake(35, self.recordContentView.top+35, 38, 60);
[self.playButton qx_layoutButtonNOSizeToFitWithEdgeInsetsStyle:(QXButtonEdgeInsetsStyleTop) imageTitleSpace:2];
[self.resetButton qx_layoutButtonNOSizeToFitWithEdgeInsetsStyle:(QXButtonEdgeInsetsStyleTop) imageTitleSpace:2];
}
-(void)authAction{
///
2025-12-04 14:11:00 +08:00
NSInteger minSecond = 60;
#if DEBUG
minSecond = 5;
# else
minSecond = 60;
#endif
if ((int)self.totalDuration < minSecond) {
NSString *toast = [NSString stringWithFormat:@"录音时长不得小于%ld秒",minSecond];
showToast(toast);
2025-11-21 16:17:05 +08:00
return;
}
if (self.originalAudioFileURL==nil) {
showToast(@"录制文件不存在");
return;
}
2025-12-04 14:11:00 +08:00
if (self.delegate && [self.delegate respondsToSelector:@selector(didClickAuthWithFileUrl:)]) {
[self.delegate didClickAuthWithFileUrl:self.originalAudioFileURL];
}
2025-11-21 16:17:05 +08:00
}
#pragma mark - Gesture Handlers
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
CGPoint touchPoint = [gesture locationInView:self];
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
[self stopPlayback];
NSLog(@"开始录制 - 第%ld段", (long)self.recordingSessionCount + 1);
self.isCancelled = NO;
[self startRecording];
break;
case UIGestureRecognizerStateChanged:
//
if (touchPoint.y < CGRectGetMinY(self.recordButton.frame) - 20) {
if (!self.isCancelled) {
NSLog(@"取消录制");
self.isCancelled = YES;
[self updateUIForCancelled];
}
} else {
if (self.isCancelled) {
NSLog(@"恢复录制");
self.isCancelled = NO;
[self updateUIForRecording];
}
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
NSLog(@"结束录制 - 当前段时长: %.1f秒", self.currentDuration);
if (self.isCancelled) {
[self cancelRecording];
} else {
[self stopRecording];
}
break;
default:
break;
}
}
#pragma mark - Recording Control
- (void)startRecording {
if (self.state == AudioRecorderStateRecording) return;
//
NSURL *audioFileURL = [self generateAudioFileURL];
NSDictionary *recordSettings = @{
AVFormatIDKey: @(kAudioFormatLinearPCM),
AVSampleRateKey: @44100.0,
AVNumberOfChannelsKey: @1,
AVEncoderAudioQualityKey: @(AVAudioQualityHigh)
};
NSError *error = nil;
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:audioFileURL settings:recordSettings error:&error];
self.audioRecorder.delegate = self;
self.audioRecorder.meteringEnabled = YES;
if (error) {
NSLog(@"录音器初始化失败: %@", error);
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didFailWithError:)]) {
[self.delegate qxAudioRecorderView:self didFailWithError:error];
}
return;
}
if ([self.audioRecorder record]) {
self.state = AudioRecorderStateRecording;
self.recordingSessionCount++;
[self startRecordingTimer];
[self updateUIForRecording];
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didChangeState:)]) {
[self.delegate qxAudioRecorderView:self didChangeState:self.state];
}
}
}
- (void)stopRecording {
if (self.state != AudioRecorderStateRecording) return;
[self.recordingTimer invalidate];
self.recordingTimer = nil;
NSTimeInterval segmentDuration = self.currentDuration;
[self.audioRecorder stop];
//
if (segmentDuration > 0.5) { // 0.5
[self.recordedSegments addObject:self.audioRecorder.url];
self.totalDuration += segmentDuration;
NSLog(@"保存音频段,时长: %.1f秒,总时长: %.1f秒", segmentDuration, self.totalDuration);
}
self.audioRecorder = nil;
self.currentDuration = 0;
self.state = AudioRecorderStateStopped;
[self updateUIForStopped];
//
if (self.recordedSegments.count > 0) {
[self mergeAudioSegments];
}
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didChangeState:)]) {
[self.delegate qxAudioRecorderView:self didChangeState:self.state];
}
}
- (void)cancelRecording {
if (self.state != AudioRecorderStateRecording) return;
[self.recordingTimer invalidate];
self.recordingTimer = nil;
[self.audioRecorder stop];
[self.audioRecorder deleteRecording];
self.audioRecorder = nil;
self.currentDuration = 0;
self.state = AudioRecorderStateStopped;
[self updateUIForStopped];
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didChangeState:)]) {
[self.delegate qxAudioRecorderView:self didChangeState:self.state];
}
}
#pragma mark - Audio Merging
- (void)mergeAudioSegments {
if (self.recordedSegments.count == 0) return;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//
NSURL *mergedFileURL = [self generateAudioFileURLWithExtension:@"m4a"];
//
AVMutableComposition *composition = [AVMutableComposition composition];
CMTime currentTime = kCMTimeZero;
for (NSURL *audioURL in self.recordedSegments) {
AVURLAsset *audioAsset = [AVURLAsset assetWithURL:audioURL];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
//
AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
AVAssetTrack *sourceAudioTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
if (sourceAudioTrack) {
[audioTrack insertTimeRange:timeRange ofTrack:sourceAudioTrack atTime:currentTime error:nil];
currentTime = CMTimeAdd(currentTime, audioAsset.duration);
}
}
//
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:composition presetName:AVAssetExportPresetAppleM4A];
exportSession.outputURL = mergedFileURL;
exportSession.outputFileType = AVFileTypeAppleM4A;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
self.originalAudioFileURL = mergedFileURL;
NSLog(@"音频合并完成,总时长: %.1f秒", self.totalDuration);
// MP3
[self convertToMP3];
} else {
NSLog(@"音频合并失败: %@", exportSession.error);
}
});
}];
});
}
#pragma mark - Reset Function
- (void)resetRecording {
NSLog(@"重置录音");
[self stopPlayback];
[self cancelRecording];
//
for (NSURL *segmentURL in self.recordedSegments) {
[[NSFileManager defaultManager] removeItemAtURL:segmentURL error:nil];
}
[self.recordedSegments removeAllObjects];
//
if (self.originalAudioFileURL) {
[[NSFileManager defaultManager] removeItemAtURL:self.originalAudioFileURL error:nil];
self.originalAudioFileURL = nil;
}
if (self.mp3AudioFileURL) {
[[NSFileManager defaultManager] removeItemAtURL:self.mp3AudioFileURL error:nil];
self.mp3AudioFileURL = nil;
}
//
self.totalDuration = 0;
self.currentDuration = 0;
self.recordingSessionCount = 0;
self.state = AudioRecorderStateReady;
[self updateUIForReady];
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didChangeState:)]) {
[self.delegate qxAudioRecorderView:self didChangeState:self.state];
}
}
#pragma mark - Playback Control
- (void)playButtonTapped {
if (self.state == AudioRecorderStatePlaying) {
[self stopPlayback];
} else {
[self playRecording];
}
}
- (void)playRecording {
if (self.state == AudioRecorderStatePlaying) return;
NSURL *playbackURL = self.mp3AudioFileURL ?: self.originalAudioFileURL;
if (!playbackURL){
showToast(@"您还未录制声音");
return;
}
//
// AVAudioSession *audioSession = [AVAudioSession sharedInstance];
// NSError *error = nil;
// [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
// [audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
// [audioSession setActive:YES error:&error];
NSError *playerError = nil;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:playbackURL error:&playerError];
self.audioPlayer.delegate = self;
if (playerError) {
NSLog(@"播放器初始化失败: %@", playerError);
return;
}
if ([self.audioPlayer play]) {
self.state = AudioRecorderStatePlaying;
[self updateUIForPlaying];
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didChangeState:)]) {
[self.delegate qxAudioRecorderView:self didChangeState:self.state];
}
}
}
- (void)stopPlayback {
if (self.state != AudioRecorderStatePlaying) return;
[self.audioPlayer stop];
self.audioPlayer = nil;
self.state = AudioRecorderStateStopped;
[self updateUIForStopped];
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didChangeState:)]) {
[self.delegate qxAudioRecorderView:self didChangeState:self.state];
}
}
- (void)resetButtonTapped {
[self resetRecording];
}
#pragma mark - Timer
- (void)startRecordingTimer {
self.currentDuration = 0;
self.recordingTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateRecordingProgress) userInfo:nil repeats:YES];
}
- (void)updateRecordingProgress {
if (self.state != AudioRecorderStateRecording) return;
self.currentDuration += 0.1;
// UI
[self updateProgress];
[self updateTimeLabel];
//
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didUpdateProgress:totalDuration:)]) {
[self.delegate qxAudioRecorderView:self didUpdateProgress:self.currentDuration totalDuration:self.totalDuration + self.currentDuration];
}
//
if ((self.totalDuration + self.currentDuration) >= self.maxDuration) {
[self stopRecording];
}
}
#pragma mark - UI Updates
- (void)updateUIForReady {
self.progressLayer.strokeEnd = 0;
self.timeLabel.text = [self formattedTime:0];
self.stateLabel.text = @"长按录音";
//
// self.playButton.hidden = YES;
// self.resetButton.hidden = YES;
}
- (void)updateUIForRecording {
self.stateLabel.text = self.isCancelled ? @"松开手指取消" : @"录音中...松开结束";
// self.playButton.hidden = YES;
// self.resetButton.hidden = YES;
}
- (void)updateUIForStopped {
self.stateLabel.text = @"继续录音";
if (self.totalDuration > 0) {
if (self.showPlaybackButton) {
// self.playButton.hidden = NO;
// self.resetButton.hidden = NO;
[self.playButton setTitle:@"试听" forState:UIControlStateNormal];
[self.playButton setImage:[UIImage imageNamed:@"singer_try_listen"] forState:UIControlStateNormal];
}
} else {
// self.playButton.hidden = YES;
// self.resetButton.hidden = YES;
}
// self.stateLabel.textColor = [UIColor grayColor];
}
- (void)updateUIForPlaying {
// self.stateLabel.text = @"试听中...";
// self.stateLabel.textColor = [UIColor blueColor];
[self.playButton setTitle:@"停止" forState:UIControlStateNormal];
[self.playButton setImage:[UIImage imageNamed:@"singer_listen_stop"] forState:UIControlStateNormal];
}
- (void)updateUIForCancelled {
// self.stateLabel.text = @"松开手指取消";
// self.stateLabel.textColor = [UIColor redColor];
}
- (void)updateProgress {
CGFloat totalProgress = (self.totalDuration + self.currentDuration) / self.maxDuration;
self.progressLayer.strokeEnd = totalProgress;
// //
// if (totalProgress > 0.8) {
// self.progressLayer.strokeColor = [UIColor redColor].CGColor;
// } else if (totalProgress > 0.6) {
// self.progressLayer.strokeColor = [UIColor orangeColor].CGColor;
// } else {
// self.progressLayer.strokeColor = [UIColor colorWithRed:0.0 green:0.48 blue:1.0 alpha:1.0].CGColor;
// }
}
- (void)updateTimeLabel {
NSTimeInterval currentTotal = self.totalDuration + self.currentDuration;
self.timeLabel.text = [self formattedTime:currentTotal];
}
#pragma mark - MP3 Conversion
- (void)convertToMP3 {
// if (!self.originalAudioFileURL) return;
//
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSString *mp3FileName = [NSString stringWithFormat:@"%@.mp3", [[NSUUID UUID] UUIDString]];
// NSString *mp3FilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:mp3FileName];
// self.mp3AudioFileURL = [NSURL fileURLWithPath:mp3FilePath];
//
// @try {
// int read, write;
//
// FILE *pcm = fopen([[self.originalAudioFileURL path] UTF8String], "rb");
// FILE *mp3 = fopen([mp3FilePath UTF8String], "wb");
//
// const int PCM_SIZE = 8192;
// const int MP3_SIZE = 8192;
// short int pcm_buffer[PCM_SIZE * 2];
// unsigned char mp3_buffer[MP3_SIZE];
//
// lame_t lame = lame_init();
// lame_set_in_samplerate(lame, 44100);
// lame_set_VBR(lame, vbr_default);
// lame_init_params(lame);
//
// do {
// read = (int)fread(pcm_buffer, 2 * sizeof(short int), PCM_SIZE, pcm);
// if (read == 0)
// write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
// else
// write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
//
// fwrite(mp3_buffer, write, 1, mp3);
//
// } while (read != 0);
//
// lame_close(lame);
// fclose(mp3);
// fclose(pcm);
// }
// @catch (NSException *exception) {
// NSLog(@"MP3转换失败: %@", exception);
// self.mp3AudioFileURL = nil;
// }
// @finally {
// dispatch_async(dispatch_get_main_queue(), ^{
// if (self.mp3AudioFileURL && [self.delegate respondsToSelector:@selector(audioRecorderView:didFinishRecordingWithMP3File:duration:)]) {
// [self.delegate audioRecorderView:self didFinishRecordingWithMP3File:self.mp3AudioFileURL duration:self.totalDuration];
// }
// });
// }
// });
}
#pragma mark - Utility Methods
- (NSURL *)generateAudioFileURL {
return [self generateAudioFileURLWithExtension:@"caf"];
}
- (NSURL *)generateAudioFileURLWithExtension:(NSString *)extension {
long long time = [[NSDate date] timeIntervalSince1970];
NSString *fileName = [NSString stringWithFormat:@"%lld.%@", time ,extension];
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
return [NSURL fileURLWithPath:filePath];
}
- (NSString *)formattedTime:(NSTimeInterval)time {
int minutes = (int)time / 60;
int seconds = (int)time % 60;
int maxMinutes = (int)_maxDuration / 60;
int maxSeconds = (int)_maxDuration % 60;
return [NSString stringWithFormat:@"%02d:%02d | %02d:%02d", minutes, seconds,maxMinutes,maxSeconds];
}
- (NSString *)getRecordingStatus {
if (self.totalDuration > 0) {
return [NSString stringWithFormat:@"已录制%.1f秒,共%ld段", self.totalDuration, (long)self.recordingSessionCount];
} else {
return @"未开始录制";
}
}
#pragma mark - AVAudioRecorderDelegate
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
NSLog(@"录音完成: %@", flag ? @"成功" : @"失败");
}
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error {
NSLog(@"录音编码错误: %@", error);
if ([self.delegate respondsToSelector:@selector(qxAudioRecorderView:didFailWithError:)]) {
[self.delegate qxAudioRecorderView:self didFailWithError:error];
}
}
#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
[self stopPlayback];
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(@"播放解码错误: %@", error);
[self stopPlayback];
}
#pragma mark - Public Properties
- (NSURL *)currentAudioFileURL {
return self.mp3AudioFileURL ?: self.originalAudioFileURL;
}
- (void)dealloc {
[self.recordingTimer invalidate];
[self.audioRecorder stop];
[self.audioPlayer stop];
}
@end