5

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

Rahul Patel
  • 1,802
  • 10
  • 21
  • @S.Karthik Nope, i can save the edited file in documents directory. But I have to play through the whole audio with the edited pitch to save the file, i want to do this is in the background without playing the audio – Rahul Patel Mar 01 '16 at 08:23
  • @S.Karthik I have searched alot, It is possible i have seen an app saving an edited audio file without playing the whole audio, but i dont know how they are doing it. I have searched and i am still searching but no luck yet – Rahul Patel Mar 01 '16 at 09:08

1 Answers1

4

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

Community
  • 1
  • 1
S. Karthik
  • 573
  • 4
  • 16
  • Will this apply the effect if we dont play the audio? – Rahul Patel Mar 02 '16 at 05:50
  • Sure i never play. I just apply the filter to the avaudioplayernode and get the buffer of data from this audioplayernode installtaponbus. Then write to the output file it works. – S. Karthik Mar 02 '16 at 05:51
  • Ok i will check it tonight. Thanks – Rahul Patel Mar 02 '16 at 05:53
  • is it compulsory to add AVAudioMixerNode? I am not adding it right now will it work without it? – Rahul Patel Mar 02 '16 at 17:36
  • in `renderAudioAndWriteToFile` method `NSTimeInterval duration = CMTimeGetSeconds(asset.duration); ` its coming 0. I applied the whole code and when i change the pitch i am calling the render method but my output file has 00:00 length any suggestion why is that happening? – Rahul Patel Mar 02 '16 at 17:52
  • ya u should add. Because in the audioinput and audiooutput we cant add effect due to single bus input. Audiomixer had multiple bus so we can add effect so and so.. – S. Karthik Mar 03 '16 at 01:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/105219/discussion-between-s-karthik-and-rahul-patel). – S. Karthik Mar 03 '16 at 06:56
  • This ALMOST works for me thank you! It's very important that ALL of the formats are set to the same value. However, I keep getting these errors when it's trying to write the file: malloc: *** mach_vm_map(size=147456) failed (error code=3) *** error: can't allocate region *** set a breakpoint in malloc_error_break to debug – user3344977 Mar 13 '16 at 12:00
  • Any idea how I can fix this? – user3344977 Mar 13 '16 at 12:00
  • I finally got this to work, thank you! It makes me call the play method on the player node though. How are you doing it without calling the play method? – user3344977 Mar 13 '16 at 13:01
  • hei cheer!...Here i just share sample codes for your reference. I too called the play method... – S. Karthik Mar 14 '16 at 04:33
  • @S.Karthik Can you please tell me where in the code have you assigned the path for the output file to be saved at? – hyd00 Aug 11 '16 at 17:39