14

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

我已经浏览了很多论坛,但无法确切了解如何实现这一目标?

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

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

我不知道如何实现这一点。请提出你的想法!

4

4 回答 4

6
  • 打开每个音频文件
  • 阅读标题信息
  • 将原始未压缩音频作为每个文件的整数数组放入内存
  • 从文件 1 的数组中要混入文件 2 的点开始,循环遍历,将文件 2 的 int 值添加到文件 1,确保“剪辑”高于或低于最大值的任何值(这就是混合音频的方式......是的,就这么简单)。如果 file2 更长,则必须使第一个数组足够长以完全容纳 file2 的其余部分。
  • 写入新的标头信息,然后写入您添加 file2 的数组中的音频。
  • 如果涉及压缩或文件不适合内存,您可能必须实施更复杂的缓冲方案。
于 2012-01-03T21:34:57.477 回答
2

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

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

如果文件很大并且需要考虑存储空间(在磁盘上),您可以考虑将 AAC 格式保存在 CAF(或简称为 .m4a)中。对于大多数应用程序,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 秒时将第二个声音写入目标缓冲区,那么您将开始在 sample number 处添加声音SampleRate * 55,其中SampleRate是您正在阅读的文件的采样率。

要混合音频,您只需使用以下形式(伪代码):

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

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

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

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

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

于 2012-01-03T22:24:35.217 回答
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;


}
于 2013-04-11T09:51:05.093 回答
0

如果您要一次播放多个声音,请务必使用 *.caf 格式。Apple 推荐它同时播放多种声音。就以编程方式混合它们而言,我假设您只是希望它们同时播放。在播放一种声音时,只需告诉另一种声音在您想要的任何时间播放即可。要设置特定时间,请使用 NSTimer ( NSTimer 类参考) 并创建一个方法以在计时器触发时播放声音。

于 2011-12-30T21:11:47.690 回答