Badam Baplan
Badam Baplan

Reputation: 270

AVAudioRecorder currentTime giving bad values

I have a setup as follows, testing on an iPhone 5s with iOS 10.3, both in and out of debug.

From my understanding, the recorder's currentTime is measured in seconds, and resets to 0.0 when recorder.isRecording reverts to false, which happens immediately when the recorder stops (this is consistent with what i see in audioRecorderDidFinishRecording)... so observing with a CADisplayLink should produce currentTime values strictly less than 5.0?

The question is: what could be going wrong here? The recorder is stopping as it should after exactly 5 seconds, but internally it thinks it recorded for more than 5 seconds? I'm listening to all my recordings and they sound fine.

I'm not sure if it's relevant, but my AVAudioSession is of type AVAudioSessionCategoryPlayAndRecord, and my audio settings are as follows (all of these excepting sample rate are necessary for later analysis):

audioSettings = [
    AVFormatIDKey: Int(kAudioFormatLinearPCM),
    AVSampleRateKey: 44100,
    AVNumberOfChannelsKey: 2,
    AVLinearPCMIsBigEndianKey: 0,
    AVLinearPCMIsFloatKey: 0,
    AVLinearPCMBitDepthKey: 16,
    AVLinearPCMIsNonInterleaved: 0
]

I already tried fiddling with all of these and didn't see any change behavior.

The CADisplayLink is added from the main thread via

recorderDisplayLink?.add(to: RunLoop.current, forMode: RunLoopMode.commonModes)

The only similar question I can find on stack exchange is this, but the question is underspecified and the answer unhelpful (for me at least).
At first I thought maybe the issue is with overloading the main queue, so that somehow the recorder's sense of time becomes bloated (which I think would still constitute bad behavior), but after disabling the animation (and also experimenting with a Timer instead of a CADisplayLink), the problem persists!!! Perhaps it is still a threading problem, but I don't see why that would be the case. And if i do need to multi-thread, I could use some help both with understanding and implementing :) Any ideas appreciated.

Upvotes: 13

Views: 2456

Answers (2)

Fergal Mohan
Fergal Mohan

Reputation: 43

I ran into the same problem for a UILabel that I was updating on a Timer but was able to workaround it by checking if the gap between the timer triggers was too big and remembering the delta as a currentRecordTimeOffset.

     if ( self.recorder.recording ) {
         float adjustedRecordTime = self.recorder.currentTime;
         if(self.currentRecordTimeOffset == 0){
             if(self.recorder.currentTime - self.lastRecordedTime > 1.0){    // current time should be updated every 0.1 so a 1 Sec delta indicates offset needed
                 self.currentRecordTimeOffset = self.recorder.currentTime;
                 adjustedRecordTime = self.recorder.currentTime - self.currentRecordTimeOffset;
             }
         }
         else{
             adjustedRecordTime = self.recorder.currentTime - self.currentRecordTimeOffset;     // get rid of the unusual offset
         }
         self.lastRecordedTime = self.recorder.currentTime;
         self.durationLabel.text = [NSString stringWithFormat:@"%.2f", adjustedRecordTime];

Upvotes: 0

Gordon Childs
Gordon Childs

Reputation: 36072

Of all the times I've used AVAudioRecorder, I've eventually had to replace it. AVAudioRecorder is a general purpose recording class, so once your requirements get a little specialised, it will disappoint you.

However, it does do metering, which is what attracts many people to it. So maybe the situation can be salvaged.

Possibilities:

a. if currentTime isn't trustworthy, then don't observe it! You've got your 5 second files, so maybe find some other way to mark that passage of time in your app.

b. of the the property currentTime, the header file says:

  only valid while recording

Are you sampling currentTime only while recording? If so that could be the problem. In which case you could use the deviceCurrentTime property, which is always valid, although you will have to subtract off the initial deviceCurrentTime.

If the situation can't be salvaged, you could pretty quickly replace AVAudioRecorder with AVAudioEngine and AVAudioFile, but that's a question for another day.

Upvotes: 4

Related Questions