Understanding AVAudioTime in AVAudioNodeTapBlock? Is there a way to get time relative to a scheduled Buffer?

I'm using AVAudioEngine to play AVAudioPCMBuffers. I'd like to synchronize some events with the playback. For example if the audio's frame position is >= some point && less than some point trigger some code.

So I'm looking at - (void)installTapOnBus:(AVAudioNodeBus)bus bufferSize:(AVAudioFrameCount)bufferSize format:(AVAudioFormat * __nullable)format block:(AVAudioNodeTapBlock)tapBlock;

Now I have frame positions calculated (predetermined before audio is scheduled I already made all necessary computations) . So I just need to fire code at certain points during playback:

[playerNode installTapOnBus:bus
                          bufferSize:bufferSize
                              format:format
                               block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) { 
      
         //Inspect current audio here and fire...
}];

 [playerNode scheduleBuffer:fullbuffer
                        atTime:startTime
                       options:0
        completionCallbackType:AVAudioPlayerNodeCompletionDataPlayedBack
             completionHandler:^(AVAudioPlayerNodeCompletionCallbackType callbackType) 
     {
          // some code is here, not important to this question.
    }];

The problem I'm having is figuring out at what point in full buffer I'm at within the tap block. The tap block passes chunks (not the full audio buffer). I tried using the when parameter of the block to calculate the frame position relative to the entire audio but have be unsuccessful so far. I'm assuming the when parameter is relative to the buffer passed in the tap block (not my entire audio buffer I scheduled).

Not installing a tap and just using a timer before scheduling my fullBuffer has given me good results but I'd rather avoid using a timer if possible and use sample time.

Looks like I needed to lower the buffer size when installing the tap block. Otherwise too many frames pass in between tap block invocations. Still not sure why a negative sample time is passed on the first call to the tap block when isSampleTimeValid is YES but that has no effect and is ignored by the code.

I think what I need is to call -playerTimeForNodeTime: in the tap block to get the time relative to the buffer scheduled on the player node.

I got pulled away from this code for awhile. Coming back to it...I noticed that the when.sampleTime in the tap block just continues to accumulate, even across multiple -scheduleBuffer:atTime:options:completionCallbackType:completionHandler: calls (with a nil time passed to atTime:).

Now if I call scheduleBuffer:atTime:options:completionCallbackType:completionHandler: and pass in a start time created like this:

AVAudioTime *startTime = [[AVAudioTime alloc]initWithSampleTime:0 atRate:22050];

The when parameter in the tap block has the same sample time as the one returned by -playerTimeForNodeTime:.

The sampleTime values in the tap block still don't seem to be accurate? For example the first time it's fired when.sampleTime is negative (though isSampleTimeValid is YES?)

I have code I want to execute when certain ranges of the buffer are reach for example:

// in the tap block
if (!when.isSampleTimeValid) { return; } 
if  (!weakToStrongPlayerNode.isPlaying) { return; }

for (TimingInfo *timing in array)
{
     if (when.sampleTime >=  timing.startFrame 
         && when.sampleTime <=  timing.endFrame)
       {
            // do something, etc. 
        }
}

But the timing just appears to be off or I'm misunderstanding how I ought to interpret when in the tap block. To verify my Timing objects have expected values I used them to slice the audio buffer into separate .wav files and got results that are accurate but I'm having a hard time getting it to work from AVAudioEngine's tap block.

Accepted Answer

Looks like I needed to lower the buffer size when installing the tap block. Otherwise too many frames pass in between tap block invocations. Still not sure why a negative sample time is passed on the first call to the tap block when isSampleTimeValid is YES but that has no effect and is ignored by the code.

Understanding AVAudioTime in AVAudioNodeTapBlock? Is there a way to get time relative to a scheduled Buffer?
 
 
Q