使用带有回调的音频文件将MP3转换为AAC [英] Converting MP3 to AAC Using Audio File Initialize With Callbacks

查看:140
本文介绍了使用带有回调的音频文件将MP3转换为AAC的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我的iOS应用程序,我需要将存储在NSData对象中的MP3文件转换为AAC格式,该文件也将存储在NSData对象中,以供日后流式传输.我还在转换之间进行了一些DSP操作,因此我利用AudioFileOpenWithCallbacks打开了现有的内存中MP3文件,并且效果很好.

For my iOS application, I need to convert an MP3 File Stored in a NSData object to AAC format which will also be stored in a NSData object to be later streamed. I'm also doing some DSP in between the conversion, so I'm taking advantage of AudioFileOpenWithCallbacks to open the existing, in-Memory, MP3 File and this works fine.

但是,我在使用AudioFileInitializeWithCallbacks(AFIWCB)创建NSMutableData对象时创建可读的AAC文件时遇到了麻烦.为了测试音频,转换完成后,我正在将NSMutableData对象写入磁盘,但是当我检查此文件上的元数据时,没有比特率或频道信息,并且该文件无法播放.文件大小大约是正确的.

However, I'm having trouble creating a read-able AAC file using AudioFileInitializeWithCallbacks (AFIWCB) to fill up an NSMutableData object. To test the audio, I'm writing the NSMutableData object to disk when the conversion is done, but when I examine the meta-data on this file, there is no bit rate or channel information and the file is un-playable. The file size is approximately correct though.

如果我跳过AFIWCB并使用ExtAudioFileCreateWithURL直接写入磁盘,则可以完美工作,尽管我的应用程序不希望写入磁盘.

And if I skip over AFIWCB and write directly to disk using ExtAudioFileCreateWithURL, it works perfectly, though writing to disk is undesirable for my application.

使用AFIWCB将音频写入内存缓冲区是否有人熟悉?文档不是很清楚,我感觉我遗漏了一些东西或没有正确使用回调.

Does anyone have any familiarity using AFIWCB to write audio to an in-memory buffer? The documentation isn't very clear and I have a feeling I'm leaving something out or not using the callbacks correctly.

感谢您可以提供的任何帮助.

Thanks for any help you can provide.

弄清楚我的问题,它在outputWriteProc回调中.在下面对其进行了修复:

Figured out my problem, it was in the outputWriteProc Callback. Fixed it below:

代码:

-(void) convertData: (NSData *) audioData {

    AudioFileID         refInputAudioFileID;            //these will be wrapped in Ext Audio File
    AudioFileID         refOutputAudioFileID;
    ExtAudioFileRef     inputFileID;                    //these deal with the actual reading and writing
    ExtAudioFileRef     outputFileID;

    // Client Audio Format Description
    AudioStreamBasicDescription clientFormat;
    memset(&clientFormat, 0, sizeof(clientFormat));
    clientFormat.mFormatID          = kAudioFormatLinearPCM;
    clientFormat.mFramesPerPacket   = 1;
    clientFormat.mChannelsPerFrame  = 2;
    clientFormat.mBitsPerChannel    = 32;
    clientFormat.mBytesPerPacket    = clientFormat.mBytesPerFrame = 4 * clientFormat.mChannelsPerFrame;
    clientFormat.mFormatFlags       = kAudioFormatFlagsNativeFloatPacked;// | kAudioFormatFlagIsNonInterleaved;
    clientFormat.mSampleRate        = 44100;

    //Output Audio Format Description
    AudioStreamBasicDescription outputFormat;
    memset(&outputFormat, 0, sizeof(outputFormat));
    outputFormat.mChannelsPerFrame  = 2;
    outputFormat.mSampleRate        = 44100;
    outputFormat.mFormatID          = kAudioFormatMPEG4AAC;
    outputFormat.mFormatFlags       = kMPEG4Object_AAC_Main;
    outputFormat.mBitsPerChannel    = 0;
    outputFormat.mBytesPerFrame     = 0;
    outputFormat.mBytesPerPacket    = 0;
    outputFormat.mFramesPerPacket   = 1024;

    //Open the Source Audio File (in Memory) and wrap it with an ExtAudioFile (this works fine)
    OSStatus result = AudioFileOpenWithCallbacks(audioData, readProc, 0, getSizeProc, 0, kAudioFileMP3Type, &refInputAudioFileID);
    if(result != noErr)
        [self CheckResult:result withMessage:@"AudioFileOpenWithCallbacks failed "];

    //2) wrap with ExtAudioFile (this works fine)
    result = ExtAudioFileWrapAudioFileID(refInputAudioFileID, false, &inputFileID);
    [self CheckResult:result withMessage:@"ExtAudioFileWrap failed for input audio "];

    UInt64 fileSizeInFrames;
    UInt32 sizeProp = sizeof(fileSizeInFrames);
    result = 0;
    result = ExtAudioFileGetProperty(inputFileID, kExtAudioFileProperty_FileLengthFrames, &sizeProp, &fileSizeInFrames);
    if(result!=noErr)
        [self CheckResult:result withMessage:@"ExtAudioFileGet Prop FileLengthFrames failed "];
    else
        sourceAudioFileSizeinFrames = fileSizeInFrames;

    //Initialize the destination audio file using NSMutableData and wrap it with ExtAudioFile (this is where I'm having problems)
    destAudioData = [[NSMutableData alloc] initWithCapacity:1000000];
    result = 0;
    result = AudioFileInitializeWithCallbacks(destAudioData, outputReadProc, outputWriteProc,
                                              getOutputSizeProc, setOutputSizeProc, kAudioFileM4AType,
                                              &outputFormat, 0, &refOutputAudioFileID);

    [self CheckResult:result withMessage:@"AudioFIleIWithCallbacks failed "];

    result = 0;
    result = ExtAudioFileWrapAudioFileID(refOutputAudioFileID, true, &outputFileID);
    [self CheckResult:result withMessage:@"ExtAudioFilWrap for dest audio failed "];

    UInt32 outputFormatSize = sizeof(outputFormat);
    result = 0;
    result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &outputFormatSize, &outputFormat);
    [self CheckResult:result withMessage:@"AudioFormatGetProp failed on output audio "];

    // Set Up Client Formats for Input
    int size = sizeof(clientFormat);
    result = 0;
    result = ExtAudioFileSetProperty(inputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
    [self CheckResult:result withMessage:@"Error on ExtAudioFileSetProperty ClientFormat on Input "];

    // Specify the software codec
    UInt32 codec = kAppleSoftwareAudioCodecManufacturer;
    result = 0;
    result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_CodecManufacturer, sizeof(UInt32), &codec);
    [self CheckResult:result withMessage:@"Error Setting Audio Codec for Output File "];

    //specify client format on output
    size = sizeof(clientFormat);
    result = 0;
    result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
    [self CheckResult:result withMessage:@"Error on ExtAudioFileSetProperty ClientDataFormat for Output File "];


    UInt64 totalFrames      = 0;
    int ioBufferSizeSamples = 1024;

    while (1) {

        UInt32 bufferByteSize       = ioBufferSizeSamples * 4 * 2;
        char srcBuffer[bufferByteSize];
        UInt32 numFrames            = (bufferByteSize/clientFormat.mBytesPerFrame);

        AudioBufferList fillBufList;
        fillBufList.mNumberBuffers  = 1;
        fillBufList.mBuffers[0].mNumberChannels     = clientFormat.mChannelsPerFrame;
        fillBufList.mBuffers[0].mDataByteSize       = bufferByteSize;
        fillBufList.mBuffers[0].mData               = srcBuffer;
        result = 0;

        //read samples
        result = ExtAudioFileRead(inputFileID, &numFrames, &fillBufList);

        if (result != noErr) {
            [self CheckResult:result withMessage:@"Error on ExtAudioFileRead for input "];
        totalFrames = 0;
            break;
          }
        if (!numFrames)
             break;

        /**********Do DSP HERE*****************/

        totalFrames = totalFrames + numFrames;

        //write output audio here
        result = 0;
        result = ExtAudioFileWrite(outputFileID,
                                   numFrames,
                                   &fillBufList);

        [self CheckResult:result withMessage:@"Error on ExtAudioFileWrite for output "];
    }

    // clean up
    result = 0;
    result = ExtAudioFileDispose(inputFileID);
    [self CheckResult:result withMessage:@"Error on ExtAudioFileDispose InputFileId "];

    result = 0;
    AudioFileClose(refInputAudioFileID);
    [self CheckResult:result withMessage:@"Error on AudioFile Clsoe InputFileId "];

    result = 0;
    ExtAudioFileDispose(outputFileID);
    [self CheckResult:result withMessage:@"Error on ExtAudioFileDispose OutputFileID "];

    result = 0;
    AudioFileClose(refOutputAudioFileID);
    [self CheckResult:result withMessage:@"Error on AudioFileClose OutputFileID "];

    //save the destination audio file here...
    NSString *destAudioPath     = [[Utils audioFilePathPrefix] stringByAppendingPathComponent:
                                   [NSString stringWithFormat:@"tone.m4a"]];

    NSURL *destURL = [NSURL fileURLWithPath:destAudioPath];

    BOOL writeOK = [destAudioData writeToURL:destURL atomically:YES];
    if(!writeOK)
        NSLog(@"problem writing the destination audio to its path \n");
}

/* *********These are the callbacks required for AudioFileOpen With Callbacks and they work fine **********/
static OSStatus readProc(void   *inClientData,
                         SInt64 position,
                         UInt32 requestCount,
                         void   *buffer,
                         UInt32 *actualCount)
{
    NSData *inAudioData     = (NSData *) inClientData;
    size_t dataSize         = inAudioData.length;
    size_t bytesToRead      = 0;

    if(position < dataSize) {
        size_t bytesAvailable = dataSize - position;
        bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable;
        [inAudioData getBytes: buffer range:NSMakeRange(position, bytesToRead)];

        *actualCount = bytesToRead;
    } else {
        NSLog(@"data was not read \n");
        bytesToRead = 0;
        *actualCount = 0;
    }
    return noErr;
}

static SInt64 getSizeProc(void* inClientData) {
    NSData *inAudioData = (NSData *) inClientData;
    size_t dataSize = inAudioData.length;
    return dataSize;
}

/**************These are the callbacks for AudioFileInitializeWithCallbacks ********/

static OSStatus outputReadProc (void   *outClientData,
                                SInt64 outputReadPosition,
                                UInt32 outputReadRequestCount,
                                void   *outputReadBuffer,
                                UInt32 *outputReadActualCount)
{        
    NSData *inAudioData     = (NSData *) outClientData;
    size_t dataSize         = inAudioData.length;
    size_t bytesToRead      = 0;

    if(outputReadPosition < dataSize) {
        size_t bytesAvailable = dataSize - outputReadPosition;
        bytesToRead = outputReadRequestCount <= bytesAvailable ? outputReadRequestCount : bytesAvailable;
        [inAudioData getBytes: outputReadBuffer range:NSMakeRange(outputReadPosition, bytesToRead)];

        *outputReadActualCount = bytesToRead;
    } else {
        bytesToRead = 0;
        *outputReadActualCount = 0;
    }
    return noErr;
}

static OSStatus outputWriteProc(void    *outClientData,
                                SInt64  writePosition,
                                UInt32  writeRequestCount,
                                const void    *writeBuffer,
                                UInt32  *writeActualCount){

NSMutableData *outAudioData = (NSMutableData *) outClientData;
UInt32 dataLen = [outAudioData length];
if(writePosition + writeRequestCount - 1 > dataLen){
    [outAudioData increaseLengthBy:(writePosition + writeRequestCount - dataLen)];
}

[outAudioData replaceBytesInRange: NSMakeRange(writePosition, writeRequestCount) withBytes: writeBuffer];

*writeActualCount  = writeRequestCount;

return noErr;
}

static SInt64 getOutputSizeProc(void *outClientData) {
    NSMutableData *inAudioData = (NSMutableData *) outClientData;
    size_t dataSize = inAudioData.length;
    return dataSize;
}

static OSStatus setOutputSizeProc(void  *outClientData, SInt64 inSize){
    NSMutableData *inAudioData = (NSMutableData *)outClientData;
    [inAudioData setLength: inSize];
    return noErr;
}

推荐答案

弄清了我的问题-我只是将数据附加到我的outputWriteProc回调中.

Figured out my problem - I was simply appending data to my outputWriteProc callback.

ExtAudioFileClose和AudioFileClose触发outputWriteProc回调以关闭文件,并且那些函数想要覆盖文件不同部分中的数据.追加数据会给我留下一个垃圾头文件和其他损坏的部分.

ExtAudioFileClose and AudioFileClose triggers the outputWriteProc callback to close the file and those functions want to overwrite data in different parts of the file. Appending the data was leaving me with a garbage header file and other corrupt parts.

这篇关于使用带有回调的音频文件将MP3转换为AAC的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆