views:

1058

answers:

1

Hi Developers,

I am struggling with converting a recorded pcm/caf file (recorded via the AudioQueue) to a m4a file. I should be possible somehow with the "AudioConverter.h" but doesn't seem to be easy at all.

If you have seen an example or have a code snippet it would be great if you can post it.

Thanks for your help

Tom

+4  A: 

Here a code snippet from what I use in real live production code for converting audio files:

- (BOOL)convertWithDocument:(SoundDocumentCore*)document selection: (SoundSelection*)selection saveTo:(NSString*)path fileType:(UInt32)fileType progress:(ProgressSheet*)progress 
{
    OSStatus        err;
    ExtAudioFileRef eafRef;
    UInt32          n;
    UInt32          dataFormat = [self dataFormat];
    SInt32          bitRate = [self bitRate];

    // -- Data Source --
    // Calculate offsets from selection, if any
    UInt64      offset = 0,   length = [document numFrames];
    SInt32      trackOffset = 0, numTracks = [document numTracks];
    if (selection != nil) {
        trackOffset = [selection firstTrack];
        numTracks  = [selection numTracks];
        offset = round ([selection startTime] * [document sampleRate]);
        length = round ([selection duration] * [document sampleRate]);
    }

    // -- Extended Audio File --
    NSString        *parentPath = [path stringByDeletingLastPathComponent];
    NSString        *fileName = [self flipColonsAndSlashes:[path lastPathComponent]];
    FSRef           parentDir;
    AudioStreamBasicDescription inputFormat, outputFormat;

    // Create FSRef from path
    err = FSPathMakeRef ((const UInt8*)[parentPath fileSystemRepresentation], &parentDir, nil);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] FSPathMakeRef() error %d", err); return NO;}

    // Delete the existing file
    [[NSFileManager defaultManager] removeFileAtPath: path handler:nil];

    // Set up the input and output data formats
    inputFormat.mSampleRate = [document sampleRate];
    inputFormat.mFormatID = kAudioFormatLinearPCM;
    inputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagsNativeEndian;
    inputFormat.mBytesPerFrame   = sizeof(float) * numTracks;
    inputFormat.mFramesPerPacket = 1;
    inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
    inputFormat.mChannelsPerFrame = numTracks;
    inputFormat.mBitsPerChannel = 32;
    inputFormat.mReserved = 0;
    [self getFileDataFormat: &outputFormat];

    // Create an audio file and then wrap it with ExtAudioFile
    AudioFileID audioFileID;
    FSRef audioFileRef;
    err = AudioFileCreate(&parentDir, (CFStringRef) fileName, fileType, &outputFormat, nil, &audioFileRef, &audioFileID);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] AudioFileCreate() error %d", err); return NO;}

    // Add user data
    [self addUserDataToAudioFile:audioFileID];

    // Wrap it in ExtAudioFile
    err = ExtAudioFileWrapAudioFileID(audioFileID, YES, &eafRef);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] ExtAudioFileWrapAudioFileID() error %d", err); return NO;}

    //err = ExtAudioFileCreateNew (&parentDir, (CFStringRef) fileName, fileType, &outputFormat, nil, &eafRef);
    //if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] ExtAudioFileCreateNew() error %d", err); return NO;}

    // Set the client data format
    err = ExtAudioFileSetProperty (eafRef, kExtAudioFileProperty_ClientDataFormat, sizeof (AudioStreamBasicDescription), &inputFormat);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] ExtAudioFileSetProperty() error %d", err); return NO;}


    // -- AudioConverter Setup --
    AudioConverterRef       converter;
    n = sizeof (converter);
    err = ExtAudioFileGetProperty (eafRef, kExtAudioFileProperty_AudioConverter, &n, &converter);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] ExtAudioFileGetProperty() error %d", err); return NO;}

    // Set quality
    UInt32  quality = kAudioCodecQuality_Max;
    err = AudioConverterSetProperty(converter, kAudioConverterEncodeBitRate, sizeof (quality), &quality);

    // Set bit rate
    if ((bitRate != 0) && (dataFormat == kAudioFormatMPEG4AAC)) {
        err = AudioConverterSetProperty(converter, kAudioConverterEncodeBitRate, sizeof (bitRate), &bitRate);
        if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] AudioConverterSetProperty() error %d", err);}
    }

    // Resynchronize ExtAudioFile with AudioConverter
    n = 0;
    err = ExtAudioFileSetProperty (eafRef, kExtAudioFileProperty_ConverterConfig, sizeof (n), &n);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] ExtAudioFileSetProperty() error %d", err);}

    // -- Write Data --
    const UInt32        maxBufferSize = 256 * 1024L;
    SInt64              remain = length;
    UInt32              frameSize  = inputFormat.mBytesPerFrame;
    UInt32              maxNumFrames = maxBufferSize / frameSize;
    NSMutableData       *bufferListData = [NSMutableData dataWithLength: 16];
    AudioBufferList     *bufferList  = [bufferListData mutableBytes];
    NSData              *bufferData;
    NSAutoreleasePool   *pool;
    BOOL                success = YES;

    // Loop
    while ((remain > 0) && (success == YES)) {
        pool = [[NSAutoreleasePool alloc] init];

        // Calculate number of frames to write
        n = (remain < maxNumFrames)? remain : maxNumFrames;

        // Get sample data from document
        bufferData = [document interleavedDataAtOffset: offset numFrames: n firstTrack: trackOffset numTracks: numTracks];

        // Set up audio buffer list
        bufferList->mNumberBuffers = 1;
        bufferList->mBuffers[0].mNumberChannels = numTracks;
        bufferList->mBuffers[0].mDataByteSize = [bufferData length];
        bufferList->mBuffers[0].mData = (void*) [bufferData bytes];

        // Write data to disk
        err = ExtAudioFileWrite (eafRef, n, bufferList);
        if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] ExtAudioFileWrite() error %d", err);}

        [pool release];

        // Update counters
        offset += n;
        remain -= n;

        // Update progress window
        [progress setMarkValue: [progress markValue] + n];
        if ([progress isCancelled]) success = NO;
    }

    // -- Clean Up --
    err = ExtAudioFileDispose (eafRef);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] ExtAudioFileDispose() error %d", err);}

    err = AudioFileClose (audioFileID);
    if (err != noErr) {if (mVerbose) NSLog(@"-[AudioFile convertWithDocument:] AudioFileClose() error %d", err);}

    return success;
}
lucius
Can you please provide some working code..
Saurabh