views:

45

answers:

2

I would like to use the Core Audio extended audio file services framework to read a mp3 file, process it as a PCM, then write the modified file back as a mp3 file. I am able to convert the mp3 file to PCM, but am NOT able to write the PCM file back as a mp3.

I have followed and analyzed the Apple ExtAudioFileConvertTest sample and also cannot get that to work. The failure point is when I set the client format for the output file(set to a canonical PCM type). This fails with error "fmt?" if the output target type is set to mp3.

Is it possible to do mp3 -> PCM -> mp3 on the iPhone? If I remove the failing line, setting the kExtAudioFileProperty_ClientDataFormat for the output file, the code fails with "pkd?" when I try to write to the output file later. So basically I have 2 errors:

1) "fmt?" when trying to set kExtAudioFileProperty_ClientDataFormat for the output file

2) "pkd?" when trying to write to the output file

Here is the code to set up the files:

NSURL *fileUrl = [NSURL fileURLWithPath:sourceFilePath];
OSStatus error = noErr;

//
// Open the file
//
error = ExtAudioFileOpenURL((CFURLRef)fileUrl, &sourceFile);

if(error){
    NSLog(@"AudioClip: Error opening file at %@.  Error code %d", sourceFilePath, error);
    return NO;
}

//
// Store the number of frames in the file
//
SInt64 numberOfFrames = 0;
UInt32 propSize = sizeof(SInt64);
error = ExtAudioFileGetProperty(sourceFile, kExtAudioFileProperty_FileLengthFrames, &propSize, &numberOfFrames);

if(error){
    NSLog(@"AudioClip: Error retreiving number of frames: %d", error);
    [self closeAudioFile];
    return NO;
}

frameCount = numberOfFrames;

//
// Get the source file format info
//
propSize = sizeof(sourceFileFormat);
memset(&sourceFileFormat, 0, sizeof(AudioStreamBasicDescription));

error = ExtAudioFileGetProperty(sourceFile, kExtAudioFileProperty_FileDataFormat, &propSize, &sourceFileFormat);

if(error){
    NSLog(@"AudioClip: Error getting source audio file properties: %d", error);
    [self closeAudioFile];
    return NO;
}

//
// Set the format for our read.  We read in PCM, clip, then write out mp3
//
memset(&readFileFormat, 0, sizeof(AudioStreamBasicDescription));

readFileFormat.mFormatID            = kAudioFormatLinearPCM;
readFileFormat.mSampleRate          = 44100;
readFileFormat.mFormatFlags         = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved;
readFileFormat.mChannelsPerFrame    = 1;
readFileFormat.mBitsPerChannel      = 8 * sizeof(AudioSampleType);
readFileFormat.mFramesPerPacket     = 1;
readFileFormat.mBytesPerFrame       = sizeof(AudioSampleType);
readFileFormat.mBytesPerPacket      = sizeof(AudioSampleType);
readFileFormat.mReserved            = 0;

propSize = sizeof(readFileFormat);
error = ExtAudioFileSetProperty(sourceFile, kExtAudioFileProperty_ClientDataFormat, propSize, &readFileFormat);

if(error){
    NSLog(@"AudioClip: Error setting read format: %d", error);
    [self closeAudioFile];
    return NO;
}

//
// Set the format for the output file that we will write
//
propSize = sizeof(targetFileFormat);
memset(&targetFileFormat, 0, sizeof(AudioStreamBasicDescription));

targetFileFormat.mFormatID          = kAudioFormatMPEGLayer3;
targetFileFormat.mChannelsPerFrame  = 1;

//
// Let the API fill in the rest
//
error = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &propSize, &targetFileFormat);

if(error){
    NSLog(@"AudioClip: Error getting target file format info: %d", error);
    [self closeAudioFile];
    return NO;
}

//
// Create our target file
//
NSURL *writeURL = [NSURL fileURLWithPath:targetFilePath];

error = ExtAudioFileCreateWithURL( (CFURLRef)writeURL, kAudioFileMP3Type, 
                                  &targetFileFormat, NULL,
                                  kAudioFileFlags_EraseFile, 
                                  &targetFile);

if(error){
    NSLog(@"AudioClip: Error opening target file for writing: %d", error);
    [self closeAudioFile];
    return NO;
}


//
// Set the client format for the output file the same as our client format for the input file
//
propSize = sizeof(readFileFormat);
error = ExtAudioFileSetProperty(targetFile, kExtAudioFileProperty_ClientDataFormat, propSize, &readFileFormat);

if(error){
    NSLog(@"AudioClip: Error, cannot set client format for output file: %d", error);
    [self closeAudioFile];
    return NO;
}

And the code to read/write:

NSInteger framesToRead = finalFrameNumber - startFrameNumber;

while(framesToRead > 0){
    //
    // Read frames into our data
    //
    short *data = (short *)malloc(framesToRead * sizeof(short));
    if(!data){
        NSLog(@"AudioPlayer: Cannot init memory for read buffer");
        [self notifyDelegateFailure];
        [self closeAudioFile];
        return;
    }

    AudioBufferList bufferList;
    OSStatus error = noErr;
    UInt32 loadedPackets = framesToRead;

    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mNumberChannels = 1;
    bufferList.mBuffers[0].mData = data;
    bufferList.mBuffers[0].mDataByteSize = (framesToRead * sizeof(short));

    NSLog(@"AudioClip: Before read nNumberBuffers = %d, mNumberChannels = %d, mData = %p, mDataByteSize = %d",
          bufferList.mNumberBuffers, bufferList.mBuffers[0].mNumberChannels, bufferList.mBuffers[0].mData,
          bufferList.mBuffers[0].mDataByteSize);

    error = ExtAudioFileRead(sourceFile, &loadedPackets, &bufferList);

    if(error){
        NSLog(@"AudioClip: Error %d from ExtAudioFileRead", error);
        [self notifyDelegateFailure];
        [self closeAudioFile];
        return;
    }

    //
    // Now write the data to our file which will convert it into a mp3 file
    //

    NSLog(@"AudioClip: After read nNumberBuffers = %d, mNumberChannels = %d, mData = %p, mDataByteSize = %d",
          bufferList.mNumberBuffers, bufferList.mBuffers[0].mNumberChannels, bufferList.mBuffers[0].mData,
          bufferList.mBuffers[0].mDataByteSize);

    error = ExtAudioFileWrite(targetFile, loadedPackets, &bufferList);

    if(error){
        NSLog(@"AudioClip: Error %d from ExtAudioFileWrite", error);
        [self notifyDelegateFailure];
        [self closeAudioFile];
        return;
    }

    framesToRead -= loadedPackets;
}
A: 

Apple doesn't supply an MP3 encoder- only a decoder. The source document is a bit outdated, but AFAIK it is still current: http://developer.apple.com/library/ios/#documentation/MusicAudio/Conceptual/CoreAudioOverview/SupportedAudioFormatsMacOSX/SupportedAudioFormatsMacOSX.html%23//apple_ref/doc/uid/TP40003577-CH7-SW1

I think your best bet might be to use AAC.

sbooth
A: 

Sorry to put this in the answer section instead of a reply, I don't have a login here yet, should prob get one.

Thanks for the quick response, that I what I thought. I've had so much trouble with core audio being irritated if the right parameter isn't set, I wasn't sure if there was a special hoop to jump.

That being said, it does seem possible to use AudioFileWritePackets to do some basic work, mainly reading and writing encoded packets to an mp3 file, but no encoding. Thanks!