Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

  • An AVAudioRecorder fires record(forDuration: 5.0)
  • A CADisplayLink watches the levels of the recorder (the recorder is meteringEnabled), synchronizing an animation, and keeping track of recorder.currentTime (but the problem can be reproduced by minimally keeping track of time in the display link)
  • The reports of recorder.currentTime consistently reach values > 5 (often 5.2 to 5.5). The results are essentially consistent with the values of recorder.deviceTime and CFAbsoluteTimeGetCurrent
  • I initialize an AVAudioPlayer and verify that the sound asset does have a duration of precisely 5.0 seconds.

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.

like image 685
Badam Baplan Avatar asked Apr 11 '17 16:04

Badam Baplan


1 Answers

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.

like image 101
Rhythmic Fistman Avatar answered Sep 29 '22 04:09

Rhythmic Fistman