【问题标题】:Joining multiple ALAC files.加入多个 ALAC 文件。
【发布时间】:2012-08-23 14:21:25
【问题描述】:

我能够加入多个 CAF 文件,其中包含编码的 PCM 音频。首先,我从一个 CAF 文件中读取数据格式。

UInt32 size = sizeof(srcFormatDescription);
        status = AudioFileGetProperty(srcAudioFile, kAudioFilePropertyDataFormat, &size, &srcFormatDescription);

PCM 数据格式如下所示

[Line 7697] mSampleRate: 44100.000000
[Line 7698] mFormatID: 1819304813d
[Line 7699] mFormatFlags: 12d
[Line 7700] mBytesPerPacket: 2d
[Line 7701] mFramesPerPacket: 1d
[Line 7702] mBytesPerFrame: 2d
[Line 7703] mChannelsPerFrame: 1d
[Line 7704] mBitsPerChannel: 16d
[Line 7705] mReserved: 0d

然后我为目标文件设置数据格式,它将包含所有 CAF。

destFormatDescription = srcFormatDescription;
status = AudioFileCreateWithURL(dest, kAudioFileCAFType, &destFormatDescription, kAudioFileFlags_EraseFile, &destAudioFile);

下一步我从 CAF 读取数据

status = AudioFileReadBytes(srcAudioFile,
                                    FALSE,
                                    currentStartForReading,
                                    &bytesNumberToRead,
                                    buffer);

并将其写入 destAudioFile

status = AudioFileWriteBytes(destAudioFile,
                                     FALSE,
                                     writePosition,
                                     &bytesNumberToWrite,
                                     buffer);

这些步骤在循环中完成。它工作得很好。 但是我有一个大问题,现在我尝试对包含 ALAC 格式数据的 CAF 文件执行相同的步骤。它不起作用。不支持 AudioFileWriteBytes 返回错误操作。

对于 ALAC 数据格式如下所示

[Line 7697] mSampleRate: 44100.000000
[Line 7698] mFormatID: 1634492771d
[Line 7699] mFormatFlags: 1d
[Line 7700] mBytesPerPacket: 0d
[Line 7701] mFramesPerPacket: 4096d
[Line 7702] mBytesPerFrame: 0d
[Line 7703] mChannelsPerFrame: 1d
[Line 7704] mBitsPerChannel: 0d
[Line 7705] mReserved: 0d

有人知道如何将多个 CAF 文件与其中的 Apple Lossless 数据合并吗?

【问题讨论】:

    标签: iphone objective-c ios macos audiotoolbox


    【解决方案1】:

    我能够加入多个 CAF 文件,其中包含编码的 PCM 音频。首先,我从一个 CAF 文件中读取数据格式。

    如果 ASBD 匹配,那么您的音频文件就是正确的。但是,如果输入文件的示例格式不匹配,那么您的数据将会损坏。这是因为每个 CAF 文件只定义了一个样本数据块和一个音频描述块。

    因此,尝试将多种格式的样本数据附加到一个音频数据块中是错误的。 (未指定是否是您正在尝试的内容)

    此外,压缩格式还有一个必须设置的附加字段 - 即魔术 cookie。所有 cookie 都必须相同(顺便说一句,这是一种不透明的数据表示)。

    最后,写入压缩数据的正确方法是使用数据包表块。 VBR/VFR 格式必须指定一个 Packet Table Chunk。

    有人知道如何将多个 CAF 文件与 Apple Lossless 数据合并在一起吗?

    这一切意味着什么:在 ALAC 的情况下,使用您选择的方法会有很高的失败率。同样,要使这些文件及其块与所涉及的所有数据包详细信息正确同步,还有很多工作要做。

    那么,让我们从另一个角度来解决这个问题:

    • 使用ExtAudioFileCreateWithURL 创建新的 ALAC 文件
    • 使用ExtAudioFileOpenURL 打开您的输入文件
    • 从您的输入文件中确定常见的示例格式
    • 使用ExtAudioFileSetProperty + kExtAudioFileProperty_ClientDataFormat 指定常用的PCM格式
    • 配置目标/ALAC 文件的转换器以使用您选择的通用 PCM 格式。
    • 读取输入文件的样本数据,写入目标
    • 重复剩余文件
    • 关闭所有文件

    应该就是这样(但如果您正在处理原始的 AudioFile API,它可能需要几百行代码)。

    【讨论】:

    • 你的方法好像没问题,我会用的。谢谢。
    • @user1212112 是的,不幸的是,这个操作并不像人们希望的那么简单——但是 ExtAudioFile 转换器将您从许多复杂性中解放出来。祝你好运,不客气。
    【解决方案2】:

    我想知道您为什么没有尝试使用 AVAssetExportSession ,AVMutableComposition, AVAsset,AVMutableCompositionTrack 。我可以建议您按照以下步骤组合/合并不同的音频文件。

    1. 使用其 URL 将每个音频文件加载到 AVAsset
    2. 使用以下代码初始化AVMutableComposition 对象。

      AVMutableComposition* mixComposition = [[AVMutableComposition alloc] init];
      
    3. 将可变音轨添加到可变组合中,如下所示。

      AVMutableCompositionTrack *AudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
      
    4. 将资产时间范围添加到AudioTrack,如下所示。

      [AudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeAdd(firstAsset.duration, secondAsset.duration)) ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
      
    5. 创建AVAssetExportSession并合并代码下面的所有音频文件。

      // Prepare final exporter for merging
      AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition AVAssetExportPresetAppleM4A];
      exporter.outputURL = outputURL;
      exporter.outputFileType = AVFileTypeAppleM4A;
      exporter.videoComposition = mainCompositionInst;
      exporter.shouldOptimizeForNetworkUse = YES;
      
      // Notify caller that merging process is about tro start
      [self.delegate handler:self didStartMergingAtPath:[outputURL path]];
      
      [exporter exportAsynchronouslyWithCompletionHandler:^ {
           dispatch_async(dispatch_get_main_queue(), ^{
               [self exportDidFinishMerging:exporter];
           });
       }];
      
    6. 还实现了检查导出过程状态的方法。

      - (void)exportDidFinishMerging:(AVAssetExportSession*)session {
      if(session.status == AVAssetExportSessionStatusCompleted){
      // notify the calling class that merging is finished
      [self.delegate handler:self didFinishMergingAtPath:[session.outputURL path]];
         }
      }
      

    注意:请注意,这不是您可以直接在代码中使用的示例代码,但这只是为了便于理解。希望这对您有所帮助。

    【讨论】:

      猜你喜欢
      • 2012-05-30
      • 1970-01-01
      • 1970-01-01
      • 2015-09-12
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 2020-10-27
      • 2017-08-11
      相关资源
      最近更新 更多