AVAudioRecorder does not create .m4a files that can be opened and played

mahboudz picture mahboudz · Jul 8, 2013 · Viewed 7.4k times · Source

I am trying to modify an iOS app that can send audio files (securely/encrypted) to other iOS devices. It uses AVAudioRecorder to record the audio files, and it uses AVAudioPlayer to play back any received audio files.

I am now trying to modify the app to create and send files that are compatible with Android. I modified the existing AVAudioRecorder code to this:

[settings setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[settings setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];
[settings setValue:[NSNumber numberWithInt:16] forKey:AVEncoderBitRateKey];
[settings setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];
[settings setValue:[NSNumber numberWithFloat:48000.0] forKey:AVSampleRateKey];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dirPath = [paths objectAtIndex:0];

NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:@"yyyy-MM-dd HH-mm"];

NSString *filename = [NSString stringWithFormat:@"%@.aac", [format stringFromDate:NSDate.date]];

The files created by this are playable on a Mac, on an iOS device, but not on an Android device. If the file extension were manually changed to end in ".m4a", then the file plays on Android.

(The advantage with the ".aac" format is that old versions of the app can receive these files and play them back — I've maintained backwards compatibility.)

So I made this change:

NSString *filename = [NSString stringWithFormat:@"%@.m4a", [format stringFromDate:NSDate.date]];

However, the resultant file (which is created with no errors from AVAudioRecorder) cannot be played on Macs nor on iOS devices. And I am waiting to hear whether it works on Android.

When AVAudioPlayer tries to open that file, it outputs:

Error Domain=NSOSStatusErrorDomain Code=1685348671 "The operation couldn’t be completed. (OSStatus error 1685348671.)" Code 1685348671 -> "dta?": The file is malformed, not a valid instance of an audio file of its type, or not recognized as an audio file.

Merely renaming the file to have a ".aac" again doesn't change the fact that it can't be loaded into anything on the Mac or iOS.

Right now, it seems like the best solution would be to create a ".aac" file, and then have the Android end replace the ".aac" with ".m4a" and try to play it.

Question 1: Does anyone know why changing the filename extension can cause AVAudioPLayer to fail? BTW, the ".m4a" file I created above, can't be opened with anything I have on the Mac so I can't tell whether the problem is with the file format or the inability of iOS or Macs to read this file type.

For anyone curious, I put a copy of an ".aac" and a ".m4a" file from AVAudioRecorder, here:

https://dl.dropboxusercontent.com/u/14939586/2013-07-08%2013-24.m4a https://dl.dropboxusercontent.com/u/14939586/2013-07-08%2010-11.aac

Question 2: While I set AVAudioRecorder to one channel, the resultant file is stereo. Why? (At least that is what VLC says about the metadata)

Question 3: Is there anyway to specify AAC-LC for lossless encoding? Or can I only do so if I move from AVAudioRecorder to a lower level API?

Answer

mahboudz picture mahboudz · Jul 9, 2013

The answer to question 1 is that you have to set the audio session correctly, otherwise the m4a file does not get formatted correctly. The audio session code that I now set, just before I start recording is:

AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryRecord error:nil];
[session setActive:YES error:nil];

I set the session back for playback after recording is finished so that audio files can be played back.

Question 2 is a problem in VLC which was displaying the incorrect number of channels. The audio file, in the MacOS Finder's Get Info window, shows up as single channel.

Question 3 is unanswered.