Я создаю простой барабанный станок, используя новые возможности AVAudioEngine в iOS 8, но у меня возникли проблемы с пониманием того, как я могу построить механизм циклирования.Построение циклического секвенсора с AVAudioEngine
Кажется, единственный способ сделать это - иметь вращающийся буфер AVAudioPlayerNodes
и иметь какое-то задание, которое постоянно планирует воспроизведение буфера в будущем. Это верно?
Было бы хорошо, если бы узлы (и их буферы) нужно было планировать только один раз. Затем узлы могли бы все reset
, затем play
снова с самого начала каждый раз, когда точка воспроизведения достигает конца последовательности.
Я пробовал последний, создавая пустой буфер образца, планируя его в конце секвенсора и прикрепляя к его completionHandler
, но он, похоже, не работает.
self.audioEngine = [[AVAudioEngine alloc] init];
self.instrumentNodes = [[NSMutableArray alloc] initWithCapacity:12];
AVAudioMixerNode *mixer = self.audioEngine.mainMixerNode;
NSError *error;
if (![self.audioEngine startAndReturnError:&error]) {
NSLog(@"Error starting engine: %@", error);
} else {
NSLog(@"Started engine");
NSURL *url = [[NSBundle mainBundle] URLForResource:@"Bleep_C3" withExtension:@"caf"];
AVAudioFile *file = [[AVAudioFile alloc] initForReading:url error:nil];
AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:file.processingFormat frameCapacity:(AVAudioFrameCount)file.length];
[file readIntoBuffer:buffer error:nil];
double sampleRate = buffer.format.sampleRate;
AVAudioPCMBuffer *blankBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:buffer.format frameCapacity:0];
AVAudioPlayerNode *loopingNode = [[AVAudioPlayerNode alloc] init];
[self.audioEngine attachNode:loopingNode];
[self.audioEngine connect:loopingNode to:mixer format:[mixer outputFormatForBus:0]];
__block void (^schedule_loops)() = ^{
for (NSUInteger i = 0; i < 16; i++) {
double sampleTime = sampleRate * (0.2 * i);
double loopTime = sampleRate * (0.2 * 16);
AVAudioPlayerNode *playerNode = [[AVAudioPlayerNode alloc] init];
[self.audioEngine attachNode:playerNode];
[self.audioEngine connect:playerNode to:mixer format:[mixer outputFormatForBus:0]];
[playerNode scheduleBuffer:buffer atTime:[AVAudioTime timeWithSampleTime:sampleTime atRate:sampleRate] options:AVAudioPlayerNodeBufferInterrupts completionHandler:^{
[playerNode stop];
[playerNode reset];
[self.audioEngine disconnectNodeOutput:playerNode];
}];
[playerNode play];
if (i == 15) {
[loopingNode scheduleBuffer:blankBuffer atTime:[AVAudioTime timeWithSampleTime:loopTime atRate:sampleRate] options:AVAudioPlayerNodeBufferInterrupts completionHandler:^{
NSLog(@"Looping");
[loopingNode stop];
schedule_loops();
}];
[loopingNode play];
}
}
};
schedule_loops();
}
Вы должны добавить к настройкам при планировании своего буфера: AVAudioPlayerNodeBufferLoops. [playerNode scheduleBuffer: buffer atTime: [AVAudioTime timeWithSampleTime: sampleTime atRate: sampleRate] опции: AVAudioPlayerNodeBufferInterrupts | AVAudioPlayerNodeBufferLoops completeHandler: nil]; – Brett