xcode 如何在 iOS 中连接 2 个或 3 个音频文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6644046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 21:26:58  来源:igfitidea点击:

How to concatenate 2 or 3 audio files in iOS?

iphonexcodeaudioxcode4

提问by EEduard

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

我是 Objective-C 的新手,只有 5 个月的 iPhone 开发经验。

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

我需要什么:
我需要将 2 个或多个音频文件连接成一个,并将结果导出为 aiff、mp3、caf 或 m4a 格式。

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.

我花了2天没有运气。那个地方是我最后的边疆。

I will very appreciate for a piece of code.

我将非常感谢一段代码。

回答by Thiru

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

输入文件:要在数组 audioIds 中提供的文件的 ID。例如。audio1.mp3、audio2.mp3 ... audioN.mp3 可在文档文件夹中使用 输出文件: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;
}

回答by Superdev

You can use this method to merge 3 sounds together.

您可以使用此方法将 3 个声音合并在一起。

- (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;


}

回答by Apple_iOS0304

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

您可能应该查看这篇文章以做同样的事情: 在目标 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.

答案与 Dimitar 的建议相似。但是您必须记住的两件重要事情是,首先 - 它仅适用于 mp3 格式,其次您尝试连接的所有文件的比特率应该相同,否则只有部分文件会在最终输出中播放。它会在比特率改变的地方停止播放。

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.

SSteve- 像 Wav 文件这样的文件有自己的标题,如果你一个接一个地写一个文件,它只会播放第一个文件然后停止播放,尽管文件信息显示更大的文件大小。这是因为我们没有将信息更新到第一个文件的标题中。

回答by Sumit Srivastava

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

我从 AVMutableCompositionTrack 中提取了两个不同的 mp3 文件。并且这两个 mp3 文件存储在同一个 AVMutableComposition 中。

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

当我按下按钮时,控制台将显示新 mp3 的路径。

-(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];

    }];

}

回答by mapboo

The easiest way to implement multiple combinations of aac:

实现aac多种组合的最简单方法:

- (NSString *)concatenatedAACVoicesPath{
NSMutableData *concatenatedData = [[NSMutableData alloc] init];

NSArray *aacPathArr = [self queryAAC];
for (NSString *path in aacPathArr) {
    NSData *data = [[NSData alloc] initWithContentsOfFile:path];
    [concatenatedData appendData: data];
}

NSString *fileNamePath = [NSString stringWithFormat:@"%@/%@.aac",[NSString createPath:currRecordDocName],currRecordDocName];
[concatenatedData writeToFile:fileNamePath atomically:YES];

return fileNamePath;

}

}

回答by SSteve

Have you tried something like this:

你有没有尝试过这样的事情:

AudioFileCreateWithURL //to create the output file
For each input file:
    AudioFileOpenURL //open file
    repeat
        AudioFileReadBytes //read from input file
        AudioFileWriteBytes //write to output file
    until eof(input file)
    AudioFileClose //close input file
AudioFileClose //close output file

This would probably require that the input files are all the same format and would create the output file in that same format. If you need to convert the format, that might be better done after creating the output file.

这可能需要所有输入文件的格式都相同,并且会以相同的格式创建输出文件。如果您需要转换格式,最好在创建输出文件后完成。

回答by Dimitar Marinov

I have an idea, not sure it will work. Try to get NSData from these 3 files, append the data into another NSData and then write it. Something like:

我有一个想法,不确定它会起作用。尝试从这 3 个文件中获取 NSData,将数据附加到另一个 NSData 中,然后将其写入。就像是:

NSMutableData *concatenatedData = [NSMutableData alloc] init];
NSData *data1 = [[NSData alloc] initWithContentsOfFile:(NSString *)path];
NSData *data2 = [[NSData alloc] initWithContentsOfFile:(NSString *)path];
NSData *data3 = [[NSData alloc] initWithContentsOfFile:(NSString *)path];
[concatenatedData appendData: data1];
[concatenatedData appendData: data2];
[concatenatedData appendData: data3];
[concatenatedData writeToFile:@"/path/to/concatenatedData.mp3" atomically:YES];

It's a theory I'm not sure it will work :), it actually works if i open an mp3 with hex editor - copy everything and paste it in the end - then I have the same sound twice. Please try it and let us know if it works.

这是一个理论,我不确定它会起作用:),如果我用十六进制编辑器打开一个 mp3,它实际上会起作用 - 复制所有内容并将其粘贴到最后 - 然后我有两次相同的声音。请尝试一下,让我们知道它是否有效。