iPhone:以编程方式混合两个音频文件?

时间:2011-12-26 19:55:04

标签: iphone ipad audio mixing

我想要两个音频文件并以编程方式混合和播放。当我播放第一个音频文件时,经过一段时间(动态时间)后,我需要在播放第一个音频文件中间的某个地方时添加带有第一个音频文件的第二个小音频文件,最后我需要保存为一个音频文件在设备上。它应该播放带有第二个混音器音频的音频文件。

我已经经历了很多论坛,但无法确切地知道如何实现这一目标?

有人可以澄清我的疑问吗?

  1. 在这种情况下,我应该使用什么音频文件/格式?我可以使用.avi文件吗?
  2. 如何在动态时间设置到第一个音频文件后以编程方式添加第二个音频?例如:如果第一个音频总时间是2分钟,我可能需要在第一个文件的1分钟或1.5分钟或55秒内混合第二个音频文件(3秒音频)。它的动态。
  3. 如何在设备上保存最终输出音频文件?如果我在某处以编程方式保存音频文件,我可以再次播放吗?
  4. 我不知道如何实现这一目标。请提出您的想法!

4 个答案:

答案 0 :(得分:6)

  • 打开每个音频文件
  • 阅读标题信息
  • 将原始未压缩音频作为每个文件的整数数组存入内存
  • 从文件1数组中要在file2中混合的点开始循环,将file2的int值添加到file1,确保“剪切”高于或低于最大值的任何值(这是混合音频的方式。 ..是的, 简单)。如果file2较长,则必须使第一个数组足够长以完全保留file2的剩余部分。
  • 编写新的标题信息,然后写入添加了file2的数组中的音频。
  • 如果涉及压缩或文件不适合内存,则可能需要实施更复杂的缓冲方案。

答案 1 :(得分:2)

  

在这种情况下,我应该使用什么音频文件/格式?我可以使用.avi文件吗?

您可以选择压缩或非压缩格式。常见的非压缩格式包括Wav和AIFF。 CAF可以表示压缩和非压缩数据。 .avi不是一个选项(由操作系统提供)。

如果文件很大并且存储空间(在磁盘上)是一个问题,您可以考虑保存在CAF(或简称.m4a)中的AAC格式。对于大多数应用程序,16位样本就足够了,您还可以通过以适当的采样率保存这些文件来节省空间,内存和CPU(参考:CD为44.1kHz)。

由于ExtAudioFile接口抽象了转换过程,因此您不必更改程序以比较分发的压缩格式和非压缩格式的大小和速度差异(CAF中的AAC对于正常应用程序可能没问题。)

非压缩CD质量音频每通道每分钟消耗约5.3 MB。因此,如果您有2个立体声音频文件,每个3分钟长,3分钟目标缓冲区,您的内存要求大约为50 MB。

由于您有“分钟”的音频,您可能需要考虑避免一次将所有音频数据加载到内存中。为了读取,操作和组合音频,您需要一个非压缩表示来在内存中工作,因此压缩格式在这里没有帮助。同样,将压缩表示转换为pcm需要大量资源;读取压缩文件虽然字节数较少,但可能需要更多(或更少)的时间。

  

如何在动态时间设置到第一个音频文件后以编程方式添加第二个音频?例如:如果第一个音频总时间是2分钟,我可能需要在第一个文件的1分钟或1.5分钟或55秒内混合第二个音频文件(3秒音频)。它的动态。

要读取文件并将其转换为您要使用的格式,请使用ExtAudioFile API - 这将为您转换为目标样本格式。内存中的常见PCM示例表示包括SInt32SInt16float,但这可能会因应用程序和硬件(iOS之外)而有很大差异。如果需要,ExtAudioFile API还可以将压缩格式转换为PCM。

您的输入音频文件应具有相同的采样率。如果没有,您将不得不重新采样音频,这是一个复杂的过程,也会占用大量资源(如果正确/准确地完成)。如果您需要支持重新采样,请将您分配的时间加倍,以完成此任务(此处不详细说明该过程)。

要添加声音,您可以从文件中请求PCM样本,处理并写入输出文件(或内存中的缓冲区)。

要确定何时添加其他声音,您需要获取输入文件的采样率(通过ExtAudioFileGetProperty)。如果您想在55秒将第二个声音写入目标缓冲区,那么您将开始添加样本号SampleRate * 55的声音,其中SampleRate是您正在阅读的文件的采样率。

要混合音频,您只需使用此表单(伪代码):

mixed[i] = fileA[i] + fileB[i];

但您必须确保避免上溢/下溢和其他算术错误。通常,您将使用某个整数值执行此过程,因为浮点计算可能需要很长时间(当有这么多时)。对于某些应用程序,您可以在不担心溢出的情况下进行移位和添加 - 这可以在添加之前有效地将每个输入减少一半。结果的幅度将是一半。如果您可以控制文件的内容(例如,它们都捆绑为资源),那么您可以简单地确保文件中没有峰值样本超过满量程值的一半(约-6dBFS)。当然,以浮动方式保存可以解决这个问题,但代价是引入更高的CPU,内存和文件i / o需求。

此时,您将打开2个文件进行读取,一个打开以进行写入,然后使用一些小的临时缓冲区来处理和混合输入,然后再写入输出文件。您应该以块为单位执行这些请求以提高效率(例如,从每个文件中读取1024个样本,处理样本,写入1024个样本)。 API并不能保证缓存和缓冲效率。

  

如何在设备上保存最终输出音频文件?如果我以某种方式以编程方式保存音频文件,我可以再次播放吗?

ExtAudioFile API可满足您的读写需求。是的,您可以稍后阅读/播放。

答案 2 :(得分:2)

你好你可以使用av基础

来做到这一点
- (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;


}

答案 3 :(得分:0)

如果您要一次播放多个声音,请务必使用* .caf格式。 Apple建议您一次播放多个声音。在以编程方式混合它们时,我假设你只是希望它们同时播放。当一个声音正在播放时,只需告诉其他声音即可在您想要的任何时间播放。要设置特定时间,请使用NSTimer(NSTimer Class Reference)并创建一种方法,以便在计时器触发时播放声音。