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 に答える