I’ve been knocking around lately with toggling bi-directional playback on streamed iPod Library tracks - successfully - using Extended Audio File Services. But I’ve run into a vexing issue with certain MP3 files and I wonder if anyone here has insight into what might be going on.
What happens is certain files - specifically, any MP3 with a bit rate of less than 320kpbs - sound garbled and distorted when streamed in reverse (they sound fine when streamed normally, i.e. forward playback). The ‘garbled’ quality is definitely reminiscent of a flawed ASBD, but I’m fairly certain that isn’t the case here as all other files - AAC, WAV, etc *and* MP3s with a bit rate of 320 - stream in both directions beautifully.
Here’s what I’m doing:
- iPod Library Access: MediaPicker -> AssetURL -> ExtAudioFileOpenURL;
- File Reading: Render Callback (on a MultiChannelMixer AU) calls a delegate method which in turn calls ExtAudioFileRead to fill the callback’s buffers… the delegate also maintains a frame index to know where its next read begins… client format is LPCM stereo interleaved float 32;
- Reverse Playback: after the read - if reverse playback is selected - I run a short while loop to reverse the order of sample frames in the filled buffer, before handing it back to the callback… I then decrement the frame index (rather than increment it), check if we’ve reached start of file, etc… here’s my code for the while loop:
Float32* revBuff = audioBufferList->mBuffers[0].mData;
int i = 0; int j = *bufferSize - 1; Float32 tmp;
while (j > i) { // works great! @autoreleasepool { tmp = revBuff[j]; revBuff[j] = revBuff[i]; revBuff[i] = tmp; j--; i++; } // end autoreleasepool }
And here are the suspects I’ve tried to rule out:
Suspect #1: my while loop is taking too long OR conversion takes longer on lower quality files, thereby leaving less time for the loop.
However, I get similar mach_absolute_time numbers for both ‘good’ files and ‘bad’ ones… here's an approximate, but representative, sampling of those numbers:
- ExtAudioFileRead (just the read, not the seek or anything else): 30000 (forward) / 90000 (reverse).
This in itself is something of a mystery: since ExtAudioFileRead always performs synchronous sequential reads in the same (forward) direction - that is, it’s doing the same work regardless of my playback direction - I would expect to see NO significant difference in timings between forward and reverse playback… but again, I’m getting these same numbers for both ‘good’ and ‘bad’ files;
- Seek times for each read: 30 or so for forward reads, 700 or so for reverse (all files);
- Callback Execution (from entry to exit): 60000 (forward) / 100000-140000 (reverse) (all files);
- Reverse Loop: 2400 (all files).
Suspect #2: Because lower bit rate MP3s contain a greater number of artifacts, perhaps these appear more prominently when the file is played in reverse (I’m grabbing at straws here but I dunno, I’m not a codec guy, maybe some Fraunhofer psychoacoustic hootchycoo is coming into play.)
OR:
Suspect #3: ExtAudioFileRead has more difficulty converting lower rate MP3s and the conversion it produces is flawed in ways that are not fully evident until you play the file in reverse (more straw grabbing).
However, I’ve tried (a) loading the original low bit MP3 into Audacity and reversing it; and (b) directly recording (within the app, using normal playback) the output of my ExtAudio-converted stream, saving it to file, then opening *that* file and streaming it in reverse: in both cases the files sound perfectly fine played backwards.
I’m getting the same playback results, btw, with the same files, on both my 64bit iPhone 6 and my 32bit iPhone 5.
The other mystery is: during reverse playback of ‘good’ files there is a CPU jump of about 14%… with bad files it is *lower* and it does a slow climb - 6-8-10%, like that - but never going higher. I might have expected the opposite.
This is really starting to dog me as I feel I must be missing something obvious… Anyone?
Best, Abel
|