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

3

12

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?

Maximin answered 8/7, 2013 at 21:32 Comment(2)
is this helpful: #4279811Surf
The accepted answer on that question is completely wrong. The file isn't an actual AAC file. It is just wrapped in an m4a. One of the other answers is correct. Also more helpful was this answer: https://mcmap.net/q/843135/-avaudiorecorder-record-aac-m4aMaximin
M
9

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.

Maximin answered 9/7, 2013 at 11:55 Comment(0)
P
3

For Create and Record audio in ".m4a" file, which play in iOS as well as in android, i use following code:

NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];

[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];


AudioRecorder = [[AVAudioRecorder alloc] initWithURL:fileOutPutUrl settings:recordSetting error:nil];
[AudioRecorder setDelegate:self];
[AudioRecorder recordForDuration:RecordLimitTime];  // Record Limit if require
if ([AudioRecorder prepareToRecord]) {
    [AudioRecorder record];
}

In Android side, i don't have to change the code for play only ".mp3" file, in android just change extension from "fileName.m4a" to "fileName.mp3", and it surprisingly works.and in iOS file = "fileName.m4a" play smoothly.

Pontic answered 7/1, 2016 at 10:3 Comment(0)
J
2

I had the same problem, I was getting NSOSStatusErrorDomain 560030580 when creating an AVAudioPlayer with a local file.

The problem was not the file, or its format, it was because I was setting up the AVAudioSession incorrectly.

I was calling setCategory(.playAndRecord) and setActive(yes) way before recording the audio, and then setCategory(.playback) and setActive(yes) when playing it, and I never called setActive(false).

According to Apple docs for some playback and recording apps the deactivation needs to happen explicitly so what I did was:

  • Call setCategory(.record) and setActive(yes) right before recording, and when finishing call setActive(false)
  • Call setCategory(.playback) and setActive(yes) right before playing the file, and when finishing call setActive(false)

Also, I found this https://www.osstatus.com/ which gives more information about the errors thrown when dealing with audio/files stuff.

Jule answered 13/3, 2021 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.