How to concatenate 2 or 3 audio files in iOS?

10,139

Solution 1

Code below can be used to merge audio files.

Input files: Ids of the files to be supplied in array audioIds. Eg. audio1.mp3, audio2.mp3 … audioN.mp3 to be available in documents folder Output file: combined.m4a

     - (BOOL) combineVoices {

       NSError *error = nil;
       BOOL ok = NO;


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


       CMTime nextClipStartTime = kCMTimeZero;
        //Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
        AVMutableComposition *composition = [[AVMutableComposition alloc] init];

     AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

     for (int i = 0; i< [self.audioIds count]; i++) {
        int key = [[self.audioIds objectAtIndex:i] intValue];
        NSString *audioFileName = [NSString stringWithFormat:@"audio%d", key];

        //Build the filename with path
        NSString *soundOne = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp3", audioFileName]];
        //NSLog(@"voice file - %@",soundOne);

        NSURL *url = [NSURL fileURLWithPath:soundOne];    
        AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
        NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
        if ([tracks count] == 0) 
            return NO;
        CMTimeRange timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [avAsset duration]);
        AVAssetTrack *clipAudioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
        ok = [compositionAudioTrack insertTimeRange:timeRangeInAsset  ofTrack:clipAudioTrack atTime:nextClipStartTime error:&error];
        if (!ok) {
            NSLog(@"Current Video Track Error: %@",error);
        }
        nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration);
    }

    // create the export session
    // no need for a retain here, the session will be retained by the
    // completion handler since it is referenced there
    AVAssetExportSession *exportSession = [AVAssetExportSession
                                           exportSessionWithAsset:composition
                                           presetName:AVAssetExportPresetAppleM4A];
    if (nil == exportSession) return NO;

    NSString *soundOneNew = [documentsDirectory stringByAppendingPathComponent:@"combined.m4a"];
    //NSLog(@"Output file path - %@",soundOneNew);

    // configure export session  output with all our parameters
    exportSession.outputURL = [NSURL fileURLWithPath:soundOneNew]; // output path
    exportSession.outputFileType = AVFileTypeAppleM4A; // output file type

    // perform the export
    [exportSession exportAsynchronouslyWithCompletionHandler:^{

        if (AVAssetExportSessionStatusCompleted == exportSession.status) {
            NSLog(@"AVAssetExportSessionStatusCompleted");
        } else if (AVAssetExportSessionStatusFailed == exportSession.status) {
            // a failure may happen because of an event out of your control
            // for example, an interruption like a phone call comming in
            // make sure and handle this case appropriately
            NSLog(@"AVAssetExportSessionStatusFailed");
        } else {
            NSLog(@"Export Session Status: %d", exportSession.status);
        }
    }];

    return YES;
}

Solution 2

You can use this method to merge 3 sounds together.

- (BOOL) combineVoices1
{
    NSError *error = nil;
    BOOL ok = NO;


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


    CMTime nextClipStartTime = kCMTimeZero;
    //Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
    AVMutableComposition *composition = [[AVMutableComposition alloc] init];

    AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [compositionAudioTrack setPreferredVolume:0.8];
    NSString *soundOne  =[[NSBundle mainBundle]pathForResource:@"test1" ofType:@"caf"];
    NSURL *url = [NSURL fileURLWithPath:soundOne];
    AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
    NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
    AVAssetTrack *clipAudioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack atTime:kCMTimeZero error:nil];

    AVMutableCompositionTrack *compositionAudioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [compositionAudioTrack setPreferredVolume:0.3];
    NSString *soundOne1  =[[NSBundle mainBundle]pathForResource:@"test" ofType:@"caf"];
    NSURL *url1 = [NSURL fileURLWithPath:soundOne1];
    AVAsset *avAsset1 = [AVURLAsset URLAssetWithURL:url1 options:nil];
    NSArray *tracks1 = [avAsset1 tracksWithMediaType:AVMediaTypeAudio];
    AVAssetTrack *clipAudioTrack1 = [[avAsset1 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    [compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack1 atTime:kCMTimeZero error:nil];


    AVMutableCompositionTrack *compositionAudioTrack2 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [compositionAudioTrack2 setPreferredVolume:1.0];
    NSString *soundOne2  =[[NSBundle mainBundle]pathForResource:@"song" ofType:@"caf"];
    NSURL *url2 = [NSURL fileURLWithPath:soundOne2];
    AVAsset *avAsset2 = [AVURLAsset URLAssetWithURL:url2 options:nil];
    NSArray *tracks2 = [avAsset2 tracksWithMediaType:AVMediaTypeAudio];
    AVAssetTrack *clipAudioTrack2 = [[avAsset2 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    [compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset2.duration) ofTrack:clipAudioTrack2 atTime:kCMTimeZero error:nil];



    AVAssetExportSession *exportSession = [AVAssetExportSession
                                           exportSessionWithAsset:composition
                                           presetName:AVAssetExportPresetAppleM4A];
    if (nil == exportSession) return NO;

    NSString *soundOneNew = [documentsDirectory stringByAppendingPathComponent:@"combined10.m4a"];
    //NSLog(@"Output file path - %@",soundOneNew);

    // configure export session  output with all our parameters
    exportSession.outputURL = [NSURL fileURLWithPath:soundOneNew]; // output path
    exportSession.outputFileType = AVFileTypeAppleM4A; // output file type

    // perform the export
    [exportSession exportAsynchronouslyWithCompletionHandler:^{

        if (AVAssetExportSessionStatusCompleted == exportSession.status) {
            NSLog(@"AVAssetExportSessionStatusCompleted");
        } else if (AVAssetExportSessionStatusFailed == exportSession.status) {
            // a failure may happen because of an event out of your control
            // for example, an interruption like a phone call comming in
            // make sure and handle this case appropriately
            NSLog(@"AVAssetExportSessionStatusFailed");
        } else {
            NSLog(@"Export Session Status: %d", exportSession.status);
        }
    }];


    return YES;


}

Solution 3

You should probably look into this post for doing the same: Combine two audio files into one in objective c

The answer is similar to what Dimitar has suggested. But 2 important things that you have to keep in mind are that, first - it works only for mp3 format and second the bit-rate of all the files that you are trying to concatenate should be same, or else only parts of your file would play in the final output. It would stop playing where the bit rate changes.

SSteve- Files like Wav files have their own headers and if you just write one file after another, It would play just the first file and would then stop playing, inspite that the file info showing a greater file size. this is because we do not have information updated into the header of the first file.

Solution 4

I have taken two diffrent mp3 files from AVMutableCompositionTrack. and these two mp3 files are stored in same AVMutableComposition.

when i will press the button the path of new mp3 will be shown by console.

-(IBAction)play
{
    [self mixAudio];    
}

-(void)mixAudio
 {
    CFAbsoluteTime currentTime=CFAbsoluteTimeGetCurrent();
    AVMutableComposition *composition = [[AVMutableComposition alloc] init];

    AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [compositionAudioTrack setPreferredVolume:0.8];
    NSString *soundOne  =[[NSBundle mainBundle]pathForResource:@"KICK1" ofType:@"mp3"];
    NSURL *url = [NSURL fileURLWithPath:soundOne];
    AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
    NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
    AVAssetTrack *clipAudioTrack = [tracks objectAtIndex:0];
    [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack atTime:kCMTimeZero error:nil];

    AVMutableCompositionTrack *compositionAudioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [compositionAudioTrack setPreferredVolume:0.8];
    NSString *soundOne1  =[[NSBundle mainBundle]pathForResource:@"KICK2" ofType:@"mp3"];
    NSURL *url1 = [NSURL fileURLWithPath:soundOne1];
    AVAsset *avAsset1 = [AVURLAsset URLAssetWithURL:url1 options:nil];
    NSArray *tracks1 = [avAsset1 tracksWithMediaType:AVMediaTypeAudio];
    AVAssetTrack *clipAudioTrack1 = [tracks1 objectAtIndex:0];
    [compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset1.duration) ofTrack:clipAudioTrack1 atTime: kCMTimeZero error:nil];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *libraryCachesDirectory = [paths objectAtIndex:0];
    NSString *strOutputFilePath = [libraryCachesDirectory stringByAppendingPathComponent:@"output.mov"];
    NSString *requiredOutputPath = [libraryCachesDirectory stringByAppendingPathComponent:@"output.m4a"];
    NSURL *audioFileOutput = [NSURL fileURLWithPath:requiredOutputPath];
    [[NSFileManager defaultManager] removeItemAtURL:audioFileOutput error:NULL];

    AVAssetExportSession *exporter=[[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];
    exporter.outputURL=audioFileOutput;
    exporter.outputFileType=AVFileTypeAppleM4A;

    [exporter exportAsynchronouslyWithCompletionHandler:^{

        NSLog(@" OUtput path is \n %@", requiredOutputPath);
        NSFileManager * fm = [[NSFileManager alloc] init];
        [fm moveItemAtPath:strOutputFilePath toPath:requiredOutputPath error:nil];

         NSLog(@" OUtput path is \n %@", requiredOutputPath);
        NSLog(@"export complete: %lf",CFAbsoluteTimeGetCurrent()-currentTime);
        NSError *error;
        audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:audioFileOutput error:&error];
        audioPlayer.numberOfLoops=0;
        [audioPlayer play];

    }];

}
Share:
10,139
EEduard
Author by

EEduard

My iOS Apps: • Voice Time • Color Food RU

Updated on June 12, 2022

Comments

  • EEduard
    EEduard almost 2 years

    I am newcomer in Objective-C and have experience only 5 months in iPhone development.

    What I need:
    I need to concatenate 2 or more audio files into one, and export result as aiff, mp3, caf or m4a format.

    For example:
    First audio file containing "You need", second "download" and third "document".
    Every audio part depends on actions from user.

    I spent 2 days without luck. That place is my last frontier.

    I will very appreciate for a piece of code.

  • EEduard
    EEduard almost 13 years
    Dimitar Marinov, thank you for your answer. I'll try. I will post here result anyway help that or not. Thank's again for your help.
  • lucasart
    lucasart almost 11 years
    Not sure if you succeeded in doing what you wanted with the above code. This doesn't work for either mp3 or m4a audio files.
  • riddhi
    riddhi over 8 years
    it return always AVAssetExportSessionStatusFailed
  • Swaminathan V
    Swaminathan V about 8 years
    is there any way to convert the resultant m4a file to mp3 file. ?
  • DOT.NET
    DOT.NET about 8 years
    what to do to run audio1 first and after this audio 2 play
  • DOT.NET
    DOT.NET about 8 years
    this play audio 1 and audio 2 together
  • Mohd Sadham
    Mohd Sadham over 7 years
    Where i your method definition of [self queryAAC]?
  • Pradeep Reddy Kypa
    Pradeep Reddy Kypa about 7 years
    This works perfectly.. Incase if you need to change the folder structure, just modify the documents directory according to your need...