Search code examples
iosaudioavaudioplayeravaudioengine

save the audio file in the background


I have an app that changes the pitch of the audio when the pitch button is selected, now i am using installTapOnBus to save the file but this method gets invocated after i press the pitch button therefore only part of the audio is saved, i want to save the whole audio no matter when the pitch button is selected, is there any way

This method is used to play the audio

-(void)playAudio
{
    NSError *err = nil;

    audioEngine = [[AVAudioEngine alloc] init];
    AudioFileplayer = [[AVAudioPlayerNode alloc] init];
    pitch = [[AVAudioUnitTimePitch alloc] init];
    reverb = [[AVAudioUnitReverb alloc] init];
    [audioEngine stop];
    [AudioFileplayer stop];
    [audioEngine reset];

    file = [[AVAudioFile alloc] initForReading:[NSURL URLWithString:[self filePath]] error:&err];


    [audioEngine attachNode:AudioFileplayer];

    [audioEngine attachNode:pitch];
    [audioEngine attachNode:reverb];

    [audioEngine connect:AudioFileplayer to:reverb format:nil];
    [audioEngine connect:reverb to:pitch format:nil];
    [audioEngine connect:pitch to:audioEngine.outputNode format:nil];
    [reverb loadFactoryPreset:AVAudioUnitReverbPresetLargeRoom2];

 [AudioFileplayer scheduleFile:file atTime:nil completionHandler:^{
        AudioFileplayer = nil;
    }];

    [audioEngine prepare];
    [audioEngine startAndReturnError:&err];

    if (err != nil) {
        NSLog(@"An error occured");
    }

    [AudioFileplayer play];



}

and this method to save the pitch effected audio

-(void)saveEffectedAudioToFolder
{
NSError *error;
if (audioEngine) {


     pitchEffect.pitch = 1000;

    AVAudioFormat  * commonFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:44100 channels:2 interleaved:NO];

    //
    AVAudioFile *outputFile = [[AVAudioFile alloc] initForWriting:[NSURL URLWithString:[self filePath1]] settings:commonFormat.settings error:&error];
    //


    if (error) {
        NSLog(@"Error is 1 %@",[error localizedDescription]);


    }
    [pitchEffect  installTapOnBus:0 bufferSize:4096 format:commonFormat block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when)
     {

         if (outputFile.length < file.length)
         {//Let us know when to stop saving the file, otherwise saving infinitely
             NSError *error;

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

         }else{
             audioEngine = nil;
             [pitchEffect removeTapOnBus:0];//if we dont remove it, will keep on tapping infinitely

         }

     }


     ];
}

}

any workaround would be helful


Solution

  • Actually we made mistake in the settings of Output Audio File. The output audio file processing format should be same like as the input file(which u put effect or pitch).

    And the Output file format should be in the wav or caf format. This format only save to the output audio file.

     - (IBAction)save_it_after_changes:(id)sender
        {
    
                 engine = [[AVAudioEngine alloc] init];
                 audio_player_node= [[AVAudioPlayerNode alloc] init];
                [engine attachNode:audio_player_node];
                [self setupEQ];
    
                AVAudioMixerNode *mixerNode = [engine mainMixerNode];
                [engine connect:audio_player_node to:unitEq format:audioFile.processingFormat];
                [engine connect:unitEq to:mixerNode format:audioFile.processingFormat];
    
                NSError *error12;
                [engine startAndReturnError:&error12];
                if (!error12)
                {
                      NSLog(@"Engine = %@",engine);
                     [audio_player_node scheduleFile:audioFile atTime:nil completionHandler:nil];
                     NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
    
                      [recordSetting setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
                      [recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
                      [recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];
    
                      [recordSetting setValue :[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
                      [recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
                      [recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey];
    
    
                      NSError *error;
                     outputFile = [[AVAudioFile alloc] initForWriting:[self testFilePathURL] settings:recordSetting error:&error];
                      NSLog(@"outputfile = %@",outputFile);
                      if (error)
                      {
                             NSLog(@"outputFile error = %@",error);
                      }
                      else
                      { //(AVAudioFrameCount)audioFile.length
                            [audio_player_node installTapOnBus:0 bufferSize:8192  format:audioFile.processingFormat block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) {
                                  NSLog(@"Buffer Size = %@",buffer);
                                  NSLog(@"when = %lld",when.sampleTime);
                                  NSLog(@"outputfile length = %lli",outputFile.length);
                                  NSLog(@"input file length = %lld",audioFile.length);
                                  if (outputFile.length<audioFile.length)
                                  {
                                        NSError *error;
                                        [outputFile writeFromBuffer:buffer error:&error];
                                        if (error)
                                        {
                                              NSLog(@"writebuffererror =%@",error);
                                        }
                                  }
                                  else
                                  {
                                        [audio_player_node removeTapOnBus:0];
                                         NSError *error2;
                                       // player2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[self testFilePathURL] error:&error2];
                                        //player2.delegate = self;
                                        NSLog(@"Pathththt = %@",[self testFilePathURL]);
                                        NSLog(@"error = %@",error2);
                                       // [audio_player_node scheduleFile:outputFile atTime:nil completionHandler:nil];
                                        //[audio_player_node play];
                                       // [self toMp3];
                                  }
    
                            }];
                      }
                }
                else
                {
                      NSLog(@"error12 =%@",error12);
                }
    
        }
    
    
    
    
    
    - (void)setupEQ
    {
          NSLog(@"setupEQ");
    
          unitEq = [[AVAudioUnitEQ alloc] initWithNumberOfBands:12];
          unitEq.globalGain = 3.0;
          AVAudioUnitEQFilterParameters *filterParameters;
          filterParameters = unitEq.bands[0];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 31;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[1];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 63;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[2];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 125;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[3];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 250;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[4];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 500;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[5];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 1000;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[6];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 2000;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[7];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 4000;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain =-20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[8];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 8000;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain = -20;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[9];
          filterParameters.filterType = AVAudioUnitEQFilterTypeParametric;
          filterParameters.frequency = 16000;
          filterParameters.bandwidth = 1.0;
          filterParameters.gain =-20;
          filterParameters.bypass = FALSE;
    
    
          filterParameters = unitEq.bands[10];
          filterParameters.filterType = AVAudioUnitEQFilterTypeLowPass;
          filterParameters.frequency = 16857;
          filterParameters.bypass = FALSE;
    
          filterParameters = unitEq.bands[11];
          filterParameters.filterType = AVAudioUnitEQFilterTypeHighPass;
          filterParameters.frequency = 205;
          filterParameters.bypass = FALSE;
          [engine attachNode:unitEq];
    }
    
    
    
    
    - (NSString *)applicationDocumentsDirectory
    {
          NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
          NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
          return basePath;
    }
    
    //------------------------------------------------------------------------------
    
    - (NSURL *)testFilePathURL
    {
          return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/test.caf",
                                         [self applicationDocumentsDirectory]]];
    }
    

    Please play the file after successfully saved. It works for me. check it out.

    Please refer below link, I get more from here, Can I use AVAudioEngine to read from a file, process with an audio unit and write to a file, faster than real-time?

    refer this sample project. It is what we are looking for https://github.com/VladimirKravchenko/AVAudioEngineOfflineRender