5 Replies
      Latest reply on May 14, 2019 2:52 AM by yarivfromcalifornia
      rodskagg Level 1 Level 1 (0 points)

        I'm trying to use AVAudioEngine to record sounds from the microphone together with various sound effect files to a AVAudioFile.

        I create an AVAudioFile like this:

         

        let settings = self.engine.mainMixerNode.outputFormatForBus(0).settings

        try self.audioFile = AVAudioFile(forWriting: self.audioURL, settings: settings, commonFormat: .PCMFormatFloat32, interleaved: false)

         

        I install a tap on the audio engine's mainMixerNode, where I write the buffer to the file:

         

        self.engine.mainMixerNode.installTapOnBus(0, bufferSize: 4096, format: self.engine.mainMixerNode.outputFormatForBus(0)) { (buffer, time) -> Void in

             do {

                  try self.audioFile?.writeFromBuffer(buffer)

             } catch let error as NSError {

                  NSLog("Error writing %@", error.localizedDescription)

             }

        }

         

        I'm using self.engine.mainMixerNode.outputFormatForBus(0).settingswhen creating the audio file since Apple states that "The buffer format MUST match the file's processing format which is why outputFormatForBus: was used when creating the AVAudioFile object above". In the documentation for installTapOnBus they also say this: " The tap and connection formats (if non-nil) on the specified bus should be identical"

         

        However, this gives me a very large, uncompressed audio file. I want to save the file as .m4a but don't understand where to specify the settings I want to use:

         

        [

        AVFormatIDKey: NSNumber(unsignedInt: kAudioFormatMPEG4AAC),

        AVSampleRateKey : NSNumber(double: 32000.0),

        AVNumberOfChannelsKey: NSNumber(int: 1),

        AVEncoderBitRatePerChannelKey: NSNumber(int: 16),

        AVEncoderAudioQualityKey: NSNumber(int: Int32(AVAudioQuality.High.rawValue))

        ]

         

        If I pass in these settings instead when creating the audio file, the app crashes when I record.

        Any suggestions or ideas on how to solve this?

        • Re: Using AVAudioEngine to record to compressed file
          umutc Level 1 Level 1 (0 points)

          Hi, sorry for late post. I'm new bie in AVAudioEngine.

          I've recorded several audio files with extension m4a.

          try this ..

           

                  NSDictionary *recordSettings = [NSDictionary dictionaryWithObjectsAndKeys:

                                                  [NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,

                                                  [NSNumber numberWithFloat:44100.0], AVSampleRateKey,

                                                  [NSNumber numberWithInt: 2], AVNumberOfChannelsKey,

                                                  nil];

             

                 AVAudioFormat  *_recordingFormat =  [[AVAudioFormat alloc] initWithSettings:recordSettings];

                ....

               //Recording func

               if (!_mixerOutputFileURL) _mixerOutputFileURL = [NSURL URLWithString:[NSTemporaryDirectory() stringByAppendingString:@"mixerOutput.m4a"]];

           

               AVAudioMixerNode *mainMixer = [_engine mainMixerNode];

           

               AVAudioFile *mixerOutputFile = [[AVAudioFile alloc] initForWriting:_mixerOutputFileURL settings:_recordingFormat.settings error:&error];

               NSAssert(mixerOutputFile != nil, @"mixerOutputFile is nil, %@", [error localizedDescription]);

           

           

           

           

               [_inputNode installTapOnBus:0 bufferSize:4096 format:[_inputNode inputFormatForBus:0] block:^(AVAudioPCMBuffer *bufferTwo, AVAudioTime *when) {

                   NSError *error;

                   NSAssert([mixerOutputFile writeFromBuffer:bufferTwo error:&error], @"error writing buffer two data to file, %@", [error localizedDescription]);

                   NSLog(@"[error localizedDescription] %@:",[error localizedDescription]);

               }];

          • Re: Using AVAudioEngine to record to compressed file
            RYounger Level 1 Level 1 (0 points)

            I have the exact same problem.  I don't mind compressing afterwards if I have to, but do you have an example of how you compressed the .caf to .m4a?

             

            -Randy

              • Re: Using AVAudioEngine to record to compressed file
                theanalogkid Apple Staff Apple Staff (610 points)

                The previous reply from "umutc" contains correct information. You can indeed write to a compressed format with AVAudioFile. I would go over your settings dictionary and look at the number of channels being passed in the AVAudioPCMBuffer.

                 

                Here's an example dictionary of settings. outputFileURL would be a URL for a file named "outputFile.m4a". The AVAudioPCMBuffer being passed to writeFromBuffer:error: should be in the files processing format which will be AVAudioPCMFormatFloat32 non-interleaved (see the note in AVAEMixerSample).

                 

                NSMutableDictionary *recordSettings = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                      [NSNumber numberWithFloat: 44100.], AVSampleRateKey,
                                                      [NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
                                                      [NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
                                                      [NSNumber numberWithInt: AVAudioQualityHigh], AVEncoderAudioQualityKey, nil];
                /*set bitrate to taste*/
                [recordSettings setObject: [NSNumber numberWithInt: 192000] forKey: AVEncoderBitRateKey];
                
                AVAudioFile *outputFile = [[AVAudioFile alloc] initForWriting:outputFileURL settings:recordSettings error:&error];
                
                

                 

                HTH

                  • Re: Using AVAudioEngine to record to compressed file
                    WestEndian Level 1 Level 1 (0 points)

                    I am having a similar problem and the above answer does not work for me.

                     

                    I am using a tap on an AVAudioEngine to get AVAudioPCMBuffer data. I save the data to a AVAudioFile using the write(from buffer: ) method.


                    I have this working correctly on an iPhone6 running IOS10.1.1 if the AVAudioFile extension is .aac. The file is readable by an AVAudioPlayer and plays correctly in other systems if I download it from the device.

                     

                    If the extension is .m4a, the AVAudioFile is initialised and the write(from buffer: ) method does not throw an error. But after recording if finished, I cannot open the m4a file. Attempting to open it throws error code 1685348671. (I've also downloaded the container from the iPhone, and checked the file directly. The file exists but it won't open because the data is malformed.)

                     

                    I'm using a standard processing format for the file. The AVAudioPCM buffer also uses this format. My audioSession is set to the RecordAndPlay category and is set to the same sample rate as the other formats in the project.

                     

                    The file format settings I use to construct the AVAudioFile are below. I've tested these settings by writing a short test project using an AVAudioRecorder and initialising it with these settings. That produces a readable m4a file.

                     

                            let recordSettings = [
                                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                                AVSampleRateKey: 22050,
                                AVNumberOfChannelsKey: 1,
                                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
                            ]
                    

                     

                    Any insight into why this might not be working?

                     

                    I'd be happy to provide more code if that would be helpful.