1

对于我的 iOS 应用程序,我需要将存储在 NSData 对象中的 MP3 文件转换为 AAC 格式,该格式也将存储在 NSData 对象中以供以后流式传输。我还在转换之间做一些 DSP,所以我利用 AudioFileOpenWithCallbacks 打开现有的内存中的 MP3 文件,这工作正常。

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

如果我跳过 AFIWCB 并使用 ExtAudioFileCreateWithURL 直接写入磁盘,它会完美运行,尽管写入磁盘对于我的应用程序来说是不可取的。

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

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

编辑:找出我的问题,它在 outputWriteProc 回调中。在下面修复它:

代码:

-(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;
}
4

1 回答 1

2

找出我的问题 - 我只是将数据附加到我的 outputWriteProc 回调。

ExtAudioFileClose 和 AudioFileClose 触发 outputWriteProc 回调来关闭文件,这些函数想要覆盖文件不同部分的数据。附加数据给我留下了一个垃圾头文件和其他损坏的部分。

于 2013-07-07T18:07:30.960 回答