我正在使用 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();
}