Вопрос

I am able to join multiple CAF files with encoded PCM audio inside. First I read data format from one of CAF files.

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

for PCM data format looks like this

[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

Then i set data format for destination file, which will contain all CAF's.

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

In next step I read data from CAF

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

and write it to destAudioFile

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

this steps are done in a loop. It works great. However I have big problem, now I try to do the same steps for CAF files, which contains data in ALAC format. It doesn't work. AudioFileWriteBytes return error operation not supported.

for ALAC data format looks like this

[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

Does anybody know how can I join multiple CAF files with Apple Lossless data inside?

Это было полезно?

Решение

I am able to join multiple CAF files with encoded PCM audio inside. First I read data format from one of CAF files.

If the ASBDs match, then your audio file would be correct. However, if the input file's sample formats do not match, then your data would be corrupt. This is because each CAF file defines exactly one sample data chunk, and one audio description chunk.

Therefore, it would be an error to attempt to append sample data of multiple formats into one audio data chunk. (whether that is or is not what you are attempting is not specified)

Furthermore, compressed formats have an additional field which must be set - that is the magic cookie. All cookies would have to be equal (this is an opaque data representation, btw).

Finally, the proper way to write compressed data is using Packet Table Chunks. The VBR/VFR formats must specify exactly one Packet Table Chunk.

Does anybody know how can I join multiple CAF files with Apple Lossless data inside?

What this all means: There will be a high failure rate using the approach you have chosen in the case of ALAC. As well, there's a lot that goes into keeping these files and their chunks correctly synchronized with all the Packet detail that is involved.

So, let's approach this from another angle:

  • use ExtAudioFileCreateWithURL to create a new ALAC file
  • use ExtAudioFileOpenURL to open your input files
  • determine a common sample format from your input files
  • use ExtAudioFileSetProperty + kExtAudioFileProperty_ClientDataFormat to specify the common PCM format
  • Configure the destination/ALAC file's convertor to expect the common PCM format you have chosen.
  • read an input file's sample data, writing to the destination
  • repeat for remaining files
  • close all files

That should be it (but it will probably require a few hundred lines of code, if you are dealing with the raw AudioFile APIs).

Другие советы

I am wondering why you haven't tried using AVAssetExportSession ,AVMutableComposition, AVAsset,AVMutableCompositionTrack . I can suggest you to follow below steps to combine/merge different audio files.

  1. Load each audio file using its URL into AVAsset.
  2. Initialize AVMutableComposition object using below code.

    AVMutableComposition* mixComposition = [[AVMutableComposition alloc] init];
    
  3. Add mutable audio track to mutable composition like below.

    AVMutableCompositionTrack *AudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    
  4. Add asset time range to AudioTrack as below.

    [AudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeAdd(firstAsset.duration, secondAsset.duration)) ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
    
  5. Create AVAssetExportSession and merge all audio files below code.

    // 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. Also implement method to check the status of export process.

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

NOTE: Please take a note that this is not sample code which you can directly use into your code but this is just for understanding purpose. Hope this may help you.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top