|
@@ -7,8 +7,11 @@
|
|
|
|
|
|
#import "KSMergeEnginePlayer.h"
|
|
#import "KSMergeEnginePlayer.h"
|
|
#import <AVFoundation/AVFoundation.h>
|
|
#import <AVFoundation/AVFoundation.h>
|
|
|
|
+#import <Accelerate/Accelerate.h>
|
|
|
|
|
|
|
|
+#define READ_FILE_LENGTH (8192)
|
|
|
|
|
|
|
|
+#define BUFFER_SIZE (2048)
|
|
|
|
|
|
@interface KSMergeEnginePlayer ()
|
|
@interface KSMergeEnginePlayer ()
|
|
/** 定时器 */
|
|
/** 定时器 */
|
|
@@ -34,24 +37,14 @@
|
|
|
|
|
|
@property (nonatomic, strong) AVAudioPCMBuffer *mixBuffer;
|
|
@property (nonatomic, strong) AVAudioPCMBuffer *mixBuffer;
|
|
|
|
|
|
-@property (nonatomic, strong) AVAudioPCMBuffer *bgBuffer;
|
|
|
|
-
|
|
|
|
-@property (nonatomic, strong) AVAudioPCMBuffer *recordBuffer;
|
|
|
|
-
|
|
|
|
@property (nonatomic, assign) AVAudioFramePosition currentFrame;
|
|
@property (nonatomic, assign) AVAudioFramePosition currentFrame;
|
|
|
|
|
|
@property (nonatomic, assign) double sampleRate;
|
|
@property (nonatomic, assign) double sampleRate;
|
|
|
|
|
|
-@property (nonatomic, assign) BOOL stopMix; // 是否停止mix
|
|
|
|
-
|
|
|
|
-@property (nonatomic, strong) dispatch_semaphore_t mixChangeSemaphore; // mix信号量
|
|
|
|
-
|
|
|
|
-@property (nonatomic, assign) BOOL stopChangeVolume; // 是否停止音量修改循环
|
|
|
|
-
|
|
|
|
-@property (nonatomic, strong) dispatch_semaphore_t volumeChangeSemaphore;
|
|
|
|
-
|
|
|
|
@property (nonatomic, assign) BOOL isInterrupt; // 是否被打断
|
|
@property (nonatomic, assign) BOOL isInterrupt; // 是否被打断
|
|
|
|
|
|
|
|
+@property (nonatomic, assign) NSInteger timeCount;
|
|
|
|
+
|
|
@end
|
|
@end
|
|
|
|
|
|
|
|
|
|
@@ -68,8 +61,6 @@
|
|
- (void)configDefault {
|
|
- (void)configDefault {
|
|
self.recordVolume = 1.0f;
|
|
self.recordVolume = 1.0f;
|
|
self.bgVolume = 1.0f;
|
|
self.bgVolume = 1.0f;
|
|
- self.mixChangeSemaphore = dispatch_semaphore_create(1); // 初始化信号量
|
|
|
|
- self.volumeChangeSemaphore = dispatch_semaphore_create(1); // 初始化信号量,初始值为1
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- (void)configEngine {
|
|
- (void)configEngine {
|
|
@@ -111,225 +102,235 @@
|
|
if (error) {
|
|
if (error) {
|
|
self.audioEngine = nil;
|
|
self.audioEngine = nil;
|
|
// 错误回调
|
|
// 错误回调
|
|
- [self sendInterruptError:error];
|
|
|
|
|
|
+ if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayerDidError:error:)]) {
|
|
|
|
+ [self.delegate enginePlayerDidError:self error:error];
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- (void)prepareNativeSongWithUrl:(NSURL *)recordAudioUrl bgMusic:(NSURL *)bgMusicUrl {
|
|
- (void)prepareNativeSongWithUrl:(NSURL *)recordAudioUrl bgMusic:(NSURL *)bgMusicUrl {
|
|
- [self loadAuidoFile:recordAudioUrl isBgm:NO];
|
|
|
|
- [self loadAuidoFile:bgMusicUrl isBgm:YES];
|
|
|
|
- self.sampleRate = self.audioFile.fileFormat.sampleRate;
|
|
|
|
- [self configEngine];
|
|
|
|
-
|
|
|
|
- AVAudioFormat *outputFormat = [self.audioEngine.mainMixerNode outputFormatForBus:0];
|
|
|
|
- [self.audioEngine connect:self.nodePlayer to:self.audioEngine.mainMixerNode format:outputFormat];
|
|
|
|
- [self startEngine];
|
|
|
|
-
|
|
|
|
- if (self.audioEngine && self.audioEngine.isRunning) {
|
|
|
|
- dispatch_async(self.sourceQueue, ^{
|
|
|
|
|
|
+ dispatch_async(self.sourceQueue, ^{
|
|
|
|
+
|
|
|
|
+ [self loadAuidoFile:recordAudioUrl isBgm:NO];
|
|
|
|
+ [self loadAuidoFile:bgMusicUrl isBgm:YES];
|
|
|
|
+ self.sampleRate = self.audioFile.fileFormat.sampleRate;
|
|
|
|
+ [self configEngine];
|
|
|
|
+
|
|
|
|
+ AVAudioFormat *outputFormat = [self.audioEngine.mainMixerNode outputFormatForBus:0];
|
|
|
|
+ [self.audioEngine connect:self.nodePlayer to:self.audioEngine.mainMixerNode format:outputFormat];
|
|
|
|
+// [self addTapBus];
|
|
|
|
+ [self startEngine];
|
|
|
|
+
|
|
|
|
+ if (self.audioEngine && self.audioEngine.isRunning) {
|
|
[self prepareBufferFrame];
|
|
[self prepareBufferFrame];
|
|
- });
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+- (void)addTapBus {
|
|
|
|
+ BOOL delegateRespondsToDidGenerateSpectrum = [self.delegate respondsToSelector:@selector(player:didGenerateSpectrum:)];
|
|
|
|
+ self.analyzer = [[KSRealtimeAnalyzer alloc] initWithFFTSize:BUFFER_SIZE];
|
|
|
|
+ AVAudioFormat *outputFormat = [self.audioEngine.mainMixerNode outputFormatForBus:0];
|
|
|
|
+ @weakObj(self);
|
|
|
|
+ [self.audioEngine.mainMixerNode removeTapOnBus:0];
|
|
|
|
+ [self.audioEngine.mainMixerNode installTapOnBus:0 bufferSize:BUFFER_SIZE format:outputFormat block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) {
|
|
|
|
+ @strongObj(self);
|
|
|
|
+ if (!self || !self.nodePlayer.isPlaying) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // 将频谱分析任务提交到后台队列
|
|
|
|
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
|
|
|
+ if (self.analyzer.isAnalise == NO) {
|
|
|
|
+ // 分析音频缓冲区
|
|
|
|
+ NSArray<NSArray<NSNumber *> *> *spectra = [self.analyzer analyseWithBuffer:buffer];
|
|
|
|
+
|
|
|
|
+ // 回到主线程更新 UI 或调用委托方法
|
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
+ if (delegateRespondsToDidGenerateSpectrum) {
|
|
|
|
+ [self.delegate player:self didGenerateSpectrum:spectra];
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }];
|
|
|
|
+}
|
|
|
|
|
|
- (void)loadAuidoFile:(NSURL *)audioFileUrl isBgm:(BOOL)isBgm {
|
|
- (void)loadAuidoFile:(NSURL *)audioFileUrl isBgm:(BOOL)isBgm {
|
|
- dispatch_sync(self.sourceQueue, ^{
|
|
|
|
- NSError *error = nil;
|
|
|
|
- AVAudioFile *audioFile = nil;
|
|
|
|
- AVAudioFormat *audioFormat = nil;
|
|
|
|
- @try {
|
|
|
|
- audioFile = [[AVAudioFile alloc] initForReading:audioFileUrl error:&error];
|
|
|
|
- audioFormat = audioFile.processingFormat;
|
|
|
|
-
|
|
|
|
- } @catch (NSException *exception) {
|
|
|
|
- audioFile = nil;
|
|
|
|
- audioFormat = nil;
|
|
|
|
- } @finally {
|
|
|
|
- if (error) {
|
|
|
|
- // 错误回调
|
|
|
|
|
|
+ NSError *error = nil;
|
|
|
|
+ AVAudioFile *audioFile = nil;
|
|
|
|
+ AVAudioFormat *audioFormat = nil;
|
|
|
|
+ @try {
|
|
|
|
+ audioFile = [[AVAudioFile alloc] initForReading:audioFileUrl error:&error];
|
|
|
|
+ audioFormat = audioFile.processingFormat;
|
|
|
|
+
|
|
|
|
+ } @catch (NSException *exception) {
|
|
|
|
+ audioFile = nil;
|
|
|
|
+ audioFormat = nil;
|
|
|
|
+ } @finally {
|
|
|
|
+ if (error) {
|
|
|
|
+ // 错误回调
|
|
|
|
+ }
|
|
|
|
+ else { // 加载成功
|
|
|
|
+ if (isBgm) {
|
|
|
|
+ self.bgAudioFile = audioFile;
|
|
|
|
+ self.bgAudioFormat = audioFormat;
|
|
}
|
|
}
|
|
- else { // 加载成功
|
|
|
|
- if (isBgm) {
|
|
|
|
- self.bgAudioFile = audioFile;
|
|
|
|
- self.bgAudioFormat = audioFormat;
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- self.audioFile = audioFile;
|
|
|
|
- self.audioFormat = audioFormat;
|
|
|
|
- }
|
|
|
|
|
|
+ else {
|
|
|
|
+ self.audioFile = audioFile;
|
|
|
|
+ self.audioFormat = audioFormat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- });
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-- (void)prepareBufferFrame {
|
|
|
|
|
|
+- (void)resetMixBuffer {
|
|
AVAudioFrameCount minFrameCount = (AVAudioFrameCount)MIN(self.bgAudioFile.length, self.audioFile.length);
|
|
AVAudioFrameCount minFrameCount = (AVAudioFrameCount)MIN(self.bgAudioFile.length, self.audioFile.length);
|
|
// mixBuffer
|
|
// mixBuffer
|
|
AVAudioFormat *outputFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:self.bgAudioFormat.sampleRate channels:2];
|
|
AVAudioFormat *outputFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:self.bgAudioFormat.sampleRate channels:2];
|
|
self.mixBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:outputFormat frameCapacity:minFrameCount];
|
|
self.mixBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:outputFormat frameCapacity:minFrameCount];
|
|
self.mixBuffer.frameLength = minFrameCount;
|
|
self.mixBuffer.frameLength = minFrameCount;
|
|
-
|
|
|
|
- self.bgBuffer = [self loadAudioSegment:self.bgAudioFile startFrame:0 frameCount:minFrameCount];
|
|
|
|
- self.recordBuffer = [self loadAudioSegment:self.audioFile startFrame:0 frameCount:minFrameCount];
|
|
|
|
-
|
|
|
|
- if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayerIsReadyPlay:)]) {
|
|
|
|
- self.isReady = YES;
|
|
|
|
- [self.delegate enginePlayerIsReadyPlay:self];
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-- (AVAudioPCMBuffer *)loadAudioSegment:(AVAudioFile *)audioFile startFrame:(AVAudioFramePosition)startFrame frameCount:(AVAudioFrameCount)frameCount {
|
|
|
|
- AVAudioFormat *audioFromat = audioFile.processingFormat;
|
|
|
|
- AVAudioFrameCount frameToRead = (AVAudioFrameCount)MIN(frameCount, (AVAudioFrameCount)audioFile.length - startFrame);
|
|
|
|
- if (startFrame > audioFile.length) {
|
|
|
|
- return nil;
|
|
|
|
- }
|
|
|
|
- AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:audioFromat frameCapacity:frameToRead];
|
|
|
|
- buffer.frameLength = frameToRead;
|
|
|
|
-
|
|
|
|
- audioFile.framePosition = startFrame;
|
|
|
|
- if (frameToRead > 0) {
|
|
|
|
- @try {
|
|
|
|
- [audioFile readIntoBuffer:buffer frameCount:frameToRead error:nil];
|
|
|
|
- } @catch (NSException *exception) {
|
|
|
|
-
|
|
|
|
- } @finally {
|
|
|
|
-
|
|
|
|
|
|
+- (void)prepareBufferFrame {
|
|
|
|
+ [self resetMixBuffer];
|
|
|
|
+ dispatch_main_async_safe(^{
|
|
|
|
+ if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayerIsReadyPlay:)]) {
|
|
|
|
+ self.isReady = YES;
|
|
|
|
+ [self.delegate enginePlayerIsReadyPlay:self];
|
|
}
|
|
}
|
|
- }
|
|
|
|
- return buffer;
|
|
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
-- (void)mixBuffers:(AVAudioPCMBuffer *)bgBuffer bgBufferVolume:(float)bgBufferVolume withRecordBuffer:(AVAudioPCMBuffer *)recordBuffer recordVolume:(float)recordVolume offset:(NSInteger)offsetTime startPosition:(AVAudioFrameCount)startPosition {
|
|
|
|
- if (!bgBuffer && !recordBuffer) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- NSLog(@"------- start");
|
|
|
|
-
|
|
|
|
- AVAudioFrameCount minFrameCount = MIN(bgBuffer.frameLength, recordBuffer.frameLength);
|
|
|
|
- AVAudioFrameCount offsetFrame = labs(offsetTime)/1000.0 * recordBuffer.format.sampleRate;
|
|
|
|
|
|
+// 预计加载buffer
|
|
|
|
+- (void)prepareBuffer:(AVAudioFramePosition)startPosition offset:(NSInteger)offsetTime mixStart:(AVAudioFramePosition)mixStartPosition {
|
|
|
|
|
|
- float *bgLeftChannel = bgBuffer.floatChannelData[0];
|
|
|
|
- float *bgRightChannel = bgBuffer.floatChannelData[1];
|
|
|
|
- // 录音文件未单声道
|
|
|
|
- float *recordLeftChannel = recordBuffer.floatChannelData[0];
|
|
|
|
-
|
|
|
|
- float *mixLeftChannel = self.mixBuffer.floatChannelData[0];
|
|
|
|
- float *mixRightChannel = self.mixBuffer.floatChannelData[1];
|
|
|
|
-
|
|
|
|
- for (int frame = 0; frame < minFrameCount; frame++) {
|
|
|
|
- if (self.stopMix) {
|
|
|
|
- NSLog(@"------- stop mix");
|
|
|
|
- dispatch_semaphore_signal(self.mixChangeSemaphore); // 释放信号量
|
|
|
|
|
|
+ dispatch_async(self.sourceQueue, ^{
|
|
|
|
+ if (!self.bgAudioFile || !self.audioFile) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- int bgFrame = frame+startPosition;
|
|
|
|
- float leftChannel = (bgFrame < bgBuffer.frameLength) ? bgLeftChannel[bgFrame] : 0;
|
|
|
|
- float rightChannel = (bgFrame < bgBuffer.frameLength) ? bgRightChannel[bgFrame] : 0;
|
|
|
|
-
|
|
|
|
- int recordFrame = (offsetTime < 0) ? (bgFrame - offsetFrame) : (bgFrame + offsetFrame);
|
|
|
|
-
|
|
|
|
- float recordData = (recordFrame >= 0 && recordFrame < recordBuffer.frameLength) ? recordLeftChannel[recordFrame] : 0;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- float mixLeftData = [self mixChannelData:leftChannel bgVolume:bgBufferVolume recordData:recordData recordVolume:recordVolume];
|
|
|
|
- float mixRightData = [self mixChannelData:rightChannel bgVolume:bgBufferVolume recordData:recordData recordVolume:recordVolume];
|
|
|
|
|
|
+ AVAudioFramePosition minFrameCount = (AVAudioFramePosition)MIN(self.bgAudioFile.length, self.audioFile.length);
|
|
|
|
+ AVAudioFrameCount offsetFrame = labs(offsetTime)/1000.0 * self.audioFile.processingFormat.sampleRate;
|
|
|
|
+ if (minFrameCount <= startPosition) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ AVAudioFrameCount frameToRead = minFrameCount - startPosition > READ_FILE_LENGTH ? READ_FILE_LENGTH : (AVAudioFrameCount)(minFrameCount - startPosition);
|
|
|
|
|
|
- mixLeftChannel[frame] = MAX(-1.0, MIN(1.0, mixLeftData));
|
|
|
|
- mixRightChannel[frame] = MAX(-1.0, MIN(1.0, mixRightData));
|
|
|
|
- }
|
|
|
|
- NSLog(@"---------finish");
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-- (float)mixChannelData:(float)bgData bgVolume:(float)bgVolume recordData:(float)recordData recordVolume:(float)recordVolume {
|
|
|
|
- return (bgData * bgVolume + recordData * recordVolume) / 2;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-- (void)changeVolume:(float)bgVolume recordVolume:(float)recordVolume {
|
|
|
|
-
|
|
|
|
- NSLog(@"bg volume ---- %f, record volume ---- %f", bgVolume, recordVolume);
|
|
|
|
- self.bgVolume = bgVolume;
|
|
|
|
- self.recordVolume = recordVolume;
|
|
|
|
- if (self.bgBuffer && self.recordBuffer) {
|
|
|
|
- self.stopChangeVolume = YES;
|
|
|
|
- // 停止上一次修改音量
|
|
|
|
- dispatch_async(self.sourceQueue, ^{
|
|
|
|
- // 等待上一次的操作完成
|
|
|
|
- dispatch_semaphore_wait(self.volumeChangeSemaphore, DISPATCH_TIME_FOREVER);
|
|
|
|
- self.stopChangeVolume = NO;
|
|
|
|
- // 开始新的音量修改操作
|
|
|
|
- AVAudioFramePosition startFrame = self.currentFrame;
|
|
|
|
- NSLog(@"----- current frame -----%lld", startFrame);
|
|
|
|
- [self modifyMixBuffer:self.bgBuffer bgBufferVolume:bgVolume withRecordBuffer:self.recordBuffer recordVolume:recordVolume offset:self.offsetTime startPosition:startFrame tagIndex:0];
|
|
|
|
- // 释放信号量,标记音量修改操作完成
|
|
|
|
- dispatch_semaphore_signal(self.volumeChangeSemaphore);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-- (void)modifyMixBuffer:(AVAudioPCMBuffer *)bgBuffer bgBufferVolume:(float)bgBufferVolume withRecordBuffer:(AVAudioPCMBuffer *)recordBuffer recordVolume:(float)recordVolume offset:(NSInteger)offsetTime startPosition:(AVAudioFramePosition)startFrame tagIndex:(NSInteger)tagIndex {
|
|
|
|
-
|
|
|
|
- AVAudioFrameCount minFrameCount = MIN(bgBuffer.frameLength, recordBuffer.frameLength);
|
|
|
|
- AVAudioFrameCount offsetFrame = labs(offsetTime)/1000.0 * recordBuffer.format.sampleRate;
|
|
|
|
-
|
|
|
|
- float *bgLeftChannel = bgBuffer.floatChannelData[0];
|
|
|
|
- float *bgRightChannel = bgBuffer.floatChannelData[1];
|
|
|
|
- // 录音文件未单声道
|
|
|
|
- float *recordLeftChannel = recordBuffer.floatChannelData[0];
|
|
|
|
-
|
|
|
|
- float *mixLeftChannel = self.mixBuffer.floatChannelData[0];
|
|
|
|
- float *mixRightChannel = self.mixBuffer.floatChannelData[1];
|
|
|
|
-
|
|
|
|
- // 先处理后续播放的buffer
|
|
|
|
- NSLog(@"------- volume change start");
|
|
|
|
- for (int frame = (int)startFrame; frame < minFrameCount; frame++) {
|
|
|
|
- if (self.stopChangeVolume) {
|
|
|
|
- NSLog(@"------- stop volume change");
|
|
|
|
- dispatch_semaphore_signal(self.volumeChangeSemaphore); // 释放信号量
|
|
|
|
|
|
+ self.bgAudioFile.framePosition = startPosition;
|
|
|
|
+ AVAudioPCMBuffer *bgBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.bgAudioFile.processingFormat frameCapacity:frameToRead];
|
|
|
|
+ bgBuffer.frameLength = frameToRead;
|
|
|
|
+ BOOL readSuccess = [self.bgAudioFile readIntoBuffer:bgBuffer frameCount:frameToRead error:nil];
|
|
|
|
+ if (!readSuccess) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+ AVAudioPCMBuffer *recordBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.audioFile.processingFormat frameCapacity:frameToRead];
|
|
|
|
+ recordBuffer.frameLength = frameToRead;
|
|
|
|
|
|
- float leftChannel = bgLeftChannel[frame];
|
|
|
|
- float rightChannel = bgRightChannel[frame];
|
|
|
|
|
|
+ if (offsetTime >= 0) { // 演奏需要提前
|
|
|
|
+ self.audioFile.framePosition = startPosition + offsetFrame;
|
|
|
|
+ AVAudioFrameCount audioReadFrame = frameToRead;
|
|
|
|
+ if (startPosition + offsetFrame + frameToRead > minFrameCount) { // 如果超出
|
|
|
|
+ audioReadFrame = (AVAudioFrameCount)(minFrameCount - startPosition - offsetFrame);
|
|
|
|
+ }
|
|
|
|
+ if (audioReadFrame <= frameToRead) {
|
|
|
|
+ BOOL isSuccess = [self.audioFile readIntoBuffer:recordBuffer frameCount:audioReadFrame error:nil];
|
|
|
|
+ if (!isSuccess) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else { // 演奏需要延后
|
|
|
|
+ AVAudioFramePosition audioFramePosition = startPosition - offsetFrame;
|
|
|
|
+ if (audioFramePosition > 0) {
|
|
|
|
+ self.audioFile.framePosition = audioFramePosition;
|
|
|
|
+ AVAudioFrameCount audioReadFrame = frameToRead;
|
|
|
|
+ if (audioFramePosition + frameToRead > minFrameCount) { // 如果超出
|
|
|
|
+ audioReadFrame = (AVAudioFrameCount)(minFrameCount - audioFramePosition);
|
|
|
|
+ }
|
|
|
|
+ // AVAudioFrameCount 无符号整型 uint32_t
|
|
|
|
+ if (audioReadFrame <= frameToRead) {
|
|
|
|
+ BOOL isSuccess = [self.audioFile readIntoBuffer:recordBuffer frameCount:audioReadFrame error:nil];
|
|
|
|
+ if (!isSuccess) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ self.audioFile.framePosition = 0;
|
|
|
|
+ // 需要读取部分数据
|
|
|
|
+ if (offsetFrame - startPosition < frameToRead) {
|
|
|
|
+ AVAudioFrameCount readCount = (AVAudioFrameCount)(offsetFrame - startPosition);
|
|
|
|
+// NSLog(@"----need readCount --%u", readCount);
|
|
|
|
+ AVAudioPCMBuffer *tempBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.audioFile.processingFormat frameCapacity:readCount];
|
|
|
|
+ tempBuffer.frameLength = readCount;
|
|
|
|
+ BOOL isSuccess = [self.audioFile readIntoBuffer:tempBuffer error:nil];
|
|
|
|
+ if (!isSuccess) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ float *tempData = tempBuffer.floatChannelData[0];
|
|
|
|
+ float *recordData = recordBuffer.floatChannelData[0];
|
|
|
|
+ // 复制数据到 recordBuffer
|
|
|
|
+ AVAudioFrameCount startFrame = frameToRead - readCount;
|
|
|
|
+ for (AVAudioFrameCount i = 0; i < readCount; i++) {
|
|
|
|
+ recordData[startFrame + i] = tempData[i];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- int recordFrame = (offsetTime < 0) ? (frame - offsetFrame) : (frame + offsetFrame);
|
|
|
|
- float recordData = (recordFrame >= 0 && recordFrame < recordBuffer.frameLength) ? recordLeftChannel[recordFrame] : 0;
|
|
|
|
|
|
+ float *bgLeftChannel = bgBuffer.floatChannelData[0];
|
|
|
|
+ float *bgRightChannel = bgBuffer.floatChannelData[1];
|
|
|
|
+ if (bgBuffer.format.channelCount == 1) {
|
|
|
|
+ bgRightChannel = bgBuffer.floatChannelData[0];
|
|
|
|
+ }
|
|
|
|
+ // 录音文件未单声道
|
|
|
|
+ float *recordLeftChannel = recordBuffer.floatChannelData[0];
|
|
|
|
|
|
- float mixLeftData = [self mixChannelData:leftChannel bgVolume:bgBufferVolume recordData:recordData recordVolume:recordVolume];
|
|
|
|
- float mixRightData = [self mixChannelData:rightChannel bgVolume:bgBufferVolume recordData:recordData recordVolume:recordVolume];
|
|
|
|
|
|
+ float *mixLeftChannel = self.mixBuffer.floatChannelData[0];
|
|
|
|
+ float *mixRightChannel = self.mixBuffer.floatChannelData[1];
|
|
|
|
|
|
- mixLeftChannel[frame-self.startPosition] = MAX(-1.0, MIN(1.0, mixLeftData));
|
|
|
|
- mixRightChannel[frame-self.startPosition] = MAX(-1.0, MIN(1.0, mixRightData));
|
|
|
|
- }
|
|
|
|
- NSLog(@"------- volume change end");
|
|
|
|
|
|
+ for (int frame = 0; frame < frameToRead; frame++) {
|
|
|
|
+
|
|
|
|
+ AVAudioFramePosition mixIndex = frame + startPosition - mixStartPosition;
|
|
|
|
+ float leftChannel = (frame < bgBuffer.frameLength) ? bgLeftChannel[frame] : 0;
|
|
|
|
+ float rightChannel = (frame < bgBuffer.frameLength) ? bgRightChannel[frame] : 0;
|
|
|
|
+
|
|
|
|
+ float recordData = (frame < recordBuffer.frameLength) ? recordLeftChannel[frame] : 0;
|
|
|
|
+
|
|
|
|
+ float mixLeftData = [self mixChannelData:leftChannel bgVolume:self.bgVolume recordData:recordData recordVolume:self.recordVolume];
|
|
|
|
+ float mixRightData = [self mixChannelData:rightChannel bgVolume:self.bgVolume recordData:recordData recordVolume:self.recordVolume];
|
|
|
|
+
|
|
|
|
+ // 防止数组越界
|
|
|
|
+ if (mixIndex >= 0 && mixIndex < self.mixBuffer.frameLength) {
|
|
|
|
+ mixLeftChannel[mixIndex] = fminf(fmaxf(mixLeftData, -1.0f), 1.0f);
|
|
|
|
+ mixRightChannel[mixIndex] = fminf(fmaxf(mixRightData, -1.0f), 1.0f);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
- (void)scheduleBufferFromPosition:(AVAudioFramePosition)startPosition {
|
|
- (void)scheduleBufferFromPosition:(AVAudioFramePosition)startPosition {
|
|
-
|
|
|
|
- self.stopMix = YES;
|
|
|
|
|
|
+ [self resetMixBuffer];
|
|
self.startPosition = startPosition;
|
|
self.startPosition = startPosition;
|
|
- dispatch_async(self.sourceQueue, ^{
|
|
|
|
- // 等待上一次的操作完成
|
|
|
|
- dispatch_semaphore_wait(self.mixChangeSemaphore, DISPATCH_TIME_FOREVER);
|
|
|
|
- self.stopMix = NO;
|
|
|
|
|
|
+ [self prepareBuffer:startPosition offset:self.offsetTime mixStart:startPosition];
|
|
|
|
+ // 加载缓冲区
|
|
|
|
+ [self.nodePlayer scheduleBuffer:self.mixBuffer atTime:nil options:AVAudioPlayerNodeBufferInterruptsAtLoop completionHandler:^{
|
|
|
|
|
|
- [self mixBuffers:self.bgBuffer bgBufferVolume:self.bgVolume withRecordBuffer:self.recordBuffer recordVolume:self.recordVolume offset:self.offsetTime startPosition:(AVAudioFrameCount)startPosition];
|
|
|
|
- // 释放信号量,标记修改操作完成
|
|
|
|
- dispatch_semaphore_signal(self.mixChangeSemaphore);
|
|
|
|
- // 加载缓冲区
|
|
|
|
- [self.nodePlayer scheduleBuffer:self.mixBuffer atTime:nil options:AVAudioPlayerNodeBufferInterruptsAtLoop completionHandler:^{
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
- });
|
|
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+- (float)mixChannelData:(float)bgData bgVolume:(float)bgVolume recordData:(float)recordData recordVolume:(float)recordVolume {
|
|
|
|
+ return (bgData * bgVolume + recordData * recordVolume) / 2;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)changeVolume:(float)bgVolume recordVolume:(float)recordVolume {
|
|
|
|
+// NSLog(@"bg volume ---- %f, record volume ---- %f", bgVolume, recordVolume);
|
|
|
|
+ self.bgVolume = bgVolume;
|
|
|
|
+ self.recordVolume = recordVolume;
|
|
|
|
+}
|
|
|
|
+
|
|
// 打断处理
|
|
// 打断处理
|
|
- (void)handleInterruption:(NSNotification *)notification {
|
|
- (void)handleInterruption:(NSNotification *)notification {
|
|
NSDictionary *info = notification.userInfo;
|
|
NSDictionary *info = notification.userInfo;
|
|
@@ -386,6 +387,7 @@
|
|
[self.delegate enginePlayerDidError:self error:error];
|
|
[self.delegate enginePlayerDidError:self error:error];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
#pragma mark ------ play action
|
|
#pragma mark ------ play action
|
|
|
|
|
|
- (void)changeRecordDelay:(NSInteger)delayMs {
|
|
- (void)changeRecordDelay:(NSInteger)delayMs {
|
|
@@ -406,8 +408,7 @@
|
|
}
|
|
}
|
|
|
|
|
|
- (void)stopPlay {
|
|
- (void)stopPlay {
|
|
- self.stopMix = YES;
|
|
|
|
- self.stopChangeVolume = YES;
|
|
|
|
|
|
+
|
|
if (self.nodePlayer.isPlaying) {
|
|
if (self.nodePlayer.isPlaying) {
|
|
[self.nodePlayer stop];
|
|
[self.nodePlayer stop];
|
|
}
|
|
}
|
|
@@ -437,9 +438,6 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // 停止修改音量循环
|
|
|
|
- self.stopChangeVolume = YES;
|
|
|
|
-
|
|
|
|
AVAudioFramePosition startFrame = startTime / 1000.0 * self.audioFormat.sampleRate;
|
|
AVAudioFramePosition startFrame = startTime / 1000.0 * self.audioFormat.sampleRate;
|
|
// 跳转进度
|
|
// 跳转进度
|
|
self.currentFrame = startFrame;
|
|
self.currentFrame = startFrame;
|
|
@@ -459,15 +457,17 @@
|
|
}
|
|
}
|
|
|
|
|
|
- (void)freePlayer {
|
|
- (void)freePlayer {
|
|
-
|
|
|
|
- if (self.nodePlayer.isPlaying) {
|
|
|
|
- [self stopPlay];
|
|
|
|
- }
|
|
|
|
|
|
+ [self stopPlay];
|
|
[self.audioEngine stop];
|
|
[self.audioEngine stop];
|
|
|
|
+ // 停止并清理定时器
|
|
|
|
+ if (_timer) {
|
|
|
|
+ [_timer invalidate];
|
|
|
|
+ _timer = nil;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- (void)startTimer {
|
|
- (void)startTimer {
|
|
-
|
|
|
|
|
|
+ self.timeCount = 0;
|
|
[self.timer setFireDate:[NSDate distantPast]];
|
|
[self.timer setFireDate:[NSDate distantPast]];
|
|
}
|
|
}
|
|
|
|
|
|
@@ -488,7 +488,7 @@
|
|
|
|
|
|
if (!_timer) {
|
|
if (!_timer) {
|
|
__weak typeof(self)weakSelf = self;
|
|
__weak typeof(self)weakSelf = self;
|
|
- _timer = [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
|
|
|
|
|
|
+ _timer = [NSTimer scheduledTimerWithTimeInterval:0.01 repeats:YES block:^(NSTimer * _Nonnull timer) {
|
|
[weakSelf timeFunction];
|
|
[weakSelf timeFunction];
|
|
}];
|
|
}];
|
|
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
|
|
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
|
|
@@ -503,12 +503,18 @@
|
|
float progress = currentTime/self.totalDuration;
|
|
float progress = currentTime/self.totalDuration;
|
|
NSDate *date = [NSDate date];
|
|
NSDate *date = [NSDate date];
|
|
NSTimeInterval inteveral = [date timeIntervalSince1970];
|
|
NSTimeInterval inteveral = [date timeIntervalSince1970];
|
|
- if (currentTime > self.totalDuration) {
|
|
|
|
|
|
+ if (currentTime >= self.totalDuration) {
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayFinished:)]) {
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(enginePlayFinished:)]) {
|
|
[self.delegate enginePlayFinished:self];
|
|
[self.delegate enginePlayFinished:self];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
|
|
+ // 定时器10ms出触发一次 buffer每100ms执行一次
|
|
|
|
+ if (self.timeCount % 10 == 0) {
|
|
|
|
+ [self scheduleMixBuffer];
|
|
|
|
+ }
|
|
|
|
+ self.timeCount++;
|
|
|
|
+
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(updatePlayProgress:andTotalTime:andProgress:currentInterval:inPlayer:)]) {
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(updatePlayProgress:andTotalTime:andProgress:currentInterval:inPlayer:)]) {
|
|
[self.delegate updatePlayProgress:currentTime andTotalTime:self.totalDuration andProgress:progress currentInterval:inteveral*1000 inPlayer:self];
|
|
[self.delegate updatePlayProgress:currentTime andTotalTime:self.totalDuration andProgress:progress currentInterval:inteveral*1000 inPlayer:self];
|
|
}
|
|
}
|
|
@@ -516,6 +522,11 @@
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+- (void)scheduleMixBuffer {
|
|
|
|
+ if (self.nodePlayer.isPlaying) {
|
|
|
|
+ [self prepareBuffer:self.currentFrame offset:self.offsetTime mixStart:self.startPosition];
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- (NSTimeInterval)getCurrentPlayTime {
|
|
- (NSTimeInterval)getCurrentPlayTime {
|
|
AVAudioTime *nodeTime = [self.nodePlayer lastRenderTime];
|
|
AVAudioTime *nodeTime = [self.nodePlayer lastRenderTime];
|
|
@@ -557,4 +568,10 @@
|
|
}
|
|
}
|
|
return NO;
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+- (void)dealloc {
|
|
|
|
+ NSLog(@"---- KSMergeEnginePlayer dealloc");
|
|
|
|
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
|
+}
|
|
|
|
+
|
|
@end
|
|
@end
|