9

iOS で WAVE ファイルを AAC エンコードされた M4A ファイルに変換する必要があります。古いデバイスやシミュレーターでは AAC エンコーディングがサポートされていないことは承知しています。コードを実行する前にテストしています。しかし、私はまだそれを機能させることができません。

Apple 独自のiPhoneExtAudioFileConvertTestの例を調べたところ、それを正確に踏襲したと思いましたが、それでもうまくいきません!

現在、宛先ファイルにクライアント形式を設定しようとすると、-50 (= ユーザー パラメータ リストのエラー) が発生します。ソースファイルでは、動作します。

以下は私のコードです。どんな助けでも大歓迎です、ありがとう!

UInt32 size;

// Open a source audio file.
ExtAudioFileRef sourceAudioFile;
ExtAudioFileOpenURL( (CFURLRef)sourceURL, &sourceAudioFile );

// Get the source data format
AudioStreamBasicDescription sourceFormat;
size = sizeof( sourceFormat );
result = ExtAudioFileGetProperty( sourceAudioFile, kExtAudioFileProperty_FileDataFormat, &size, &sourceFormat );

// Define the output format (AAC).
AudioStreamBasicDescription outputFormat;
outputFormat.mFormatID = kAudioFormatMPEG4AAC;
outputFormat.mSampleRate = 44100;
outputFormat.mChannelsPerFrame = 2;

// Use AudioFormat API to fill out the rest of the description.
size = sizeof( outputFormat );
AudioFormatGetProperty( kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat);

// Make a destination audio file with this output format.
ExtAudioFileRef destAudioFile;
ExtAudioFileCreateWithURL( (CFURLRef)destURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &destAudioFile );

 // Create canonical PCM client format.
AudioStreamBasicDescription clientFormat;
clientFormat.mSampleRate = sourceFormat.mSampleRate;
clientFormat.mFormatID = kAudioFormatLinearPCM;
clientFormat.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
clientFormat.mChannelsPerFrame = 2;
clientFormat.mBitsPerChannel = 16;
clientFormat.mBytesPerFrame = 4;
clientFormat.mBytesPerPacket = 4;
clientFormat.mFramesPerPacket = 1;

// Set the client format in source and destination file.
size = sizeof( clientFormat );
ExtAudioFileSetProperty( sourceAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat );
size = sizeof( clientFormat );
ExtAudioFileSetProperty( destAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat );

// Make a buffer
int bufferSizeInFrames = 8000;
int bufferSize = ( bufferSizeInFrames * sourceFormat.mBytesPerFrame );
UInt8 * buffer = (UInt8 *)malloc( bufferSize );
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame;
bufferList.mBuffers[0].mData = buffer;
bufferList.mBuffers[0].mDataByteSize = ( bufferSize );

while( TRUE )
{
    // Try to fill the buffer to capacity.
    UInt32 framesRead = bufferSizeInFrames;
    ExtAudioFileRead( sourceAudioFile, &framesRead, &bufferList );

    // 0 frames read means EOF.
    if( framesRead == 0 )
        break;

    // Write.
    ExtAudioFileWrite( destAudioFile, framesRead, &bufferList );
}

free( buffer );

// Close the files.
ExtAudioFileDispose( sourceAudioFile );
ExtAudioFileDispose( destAudioFile );
4

3 に答える 3

13

私自身の質問に答えました:私はこの問題を同僚に渡さなければなりませんでした、そして彼はそれを働かせました! 元の問題を分析する機会はありませんでしたが、完全を期すためにここに投稿すると思いました。次のメソッドは、NSThread 内から呼び出されます。パラメーターは「threadDictionary」を介して設定され、彼は進行状況のフィードバックを送信するカスタム デリゲートを作成しました (申し訳ありませんが、SO はフォーマットを適切に理解していません。以下はメソッド実装の 1 つのブロックであると想定されています)。

- (void)encodeToAAC
{
    RXAudioEncoderStatusType encoderStatus;
    OSStatus result = noErr;
    BOOL success = NO;
    BOOL cancelled = NO;
    UInt32 size;

    ExtAudioFileRef sourceAudioFile,destAudioFile;
    AudioStreamBasicDescription sourceFormat,outputFormat, clientFormat;

    SInt64 totalFrames;
    unsigned long long encodedBytes, totalBytes;

    int bufferSizeInFrames, bufferSize;
    UInt8 * buffer;
    AudioBufferList bufferList;

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSFileManager * fileManager = [[[NSFileManager alloc] init] autorelease];

    NSMutableDictionary * threadDict = [[NSThread currentThread] threadDictionary];

    NSObject<RXAudioEncodingDelegate> * delegate = (NSObject<RXAudioEncodingDelegate> *)[threadDict objectForKey:@"Delegate"];

    NSString *sourcePath = (NSString *)[threadDict objectForKey:@"SourcePath"];
    NSString *destPath = (NSString *)[threadDict objectForKey:@"DestinationPath"];

    NSURL * sourceURL = [NSURL fileURLWithPath:sourcePath];
    NSURL * destURL = [NSURL fileURLWithPath:destPath];

    // Open a source audio file.
    result = ExtAudioFileOpenURL( (CFURLRef)sourceURL, &sourceAudioFile );
    if( result != noErr )
    {
        DLog( @"Error in ExtAudioFileOpenURL: %ld", result );
        goto bailout;
    }

    // Get the source data format
    size = sizeof( sourceFormat );
    result = ExtAudioFileGetProperty( sourceAudioFile, kExtAudioFileProperty_FileDataFormat, &size, &sourceFormat );
    if( result != noErr )
    {
        DLog( @"Error in ExtAudioFileGetProperty: %ld", result );
        goto bailout;
    }

    // Define the output format (AAC).
    memset(&outputFormat, 0, sizeof(outputFormat));
    outputFormat.mFormatID = kAudioFormatMPEG4AAC;
    outputFormat.mSampleRate = 44100;
    outputFormat.mFormatFlags = kMPEG4Object_AAC_Main;
    outputFormat.mChannelsPerFrame = 2;
    outputFormat.mBitsPerChannel = 0;
    outputFormat.mBytesPerFrame = 0;
    outputFormat.mBytesPerPacket = 0;
    outputFormat.mFramesPerPacket = 1024;


    // Use AudioFormat API to fill out the rest of the description.
    //size = sizeof( outputFormat );
    //AudioFormatGetProperty( kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat);

    // Make a destination audio file with this output format.
    result = ExtAudioFileCreateWithURL( (CFURLRef)destURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &destAudioFile );
    if( result != noErr )
    {
        DLog( @"Error creating destination file: %ld", result );
        goto bailout;
    }

    // Create the canonical PCM client format.
    memset(&clientFormat, 0, sizeof(clientFormat));
    clientFormat.mSampleRate = sourceFormat.mSampleRate;
    clientFormat.mFormatID = kAudioFormatLinearPCM;
    clientFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; //kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
    clientFormat.mChannelsPerFrame = 2;
    clientFormat.mBitsPerChannel = 16;
    clientFormat.mBytesPerFrame = 4;
    clientFormat.mBytesPerPacket = 4;
    clientFormat.mFramesPerPacket = 1;

    // Set the client format in source and destination file.
    size = sizeof( clientFormat );
    result = ExtAudioFileSetProperty( sourceAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat );
    if( result != noErr )
    {
        DLog( @"Error while setting client format in source file: %ld", result );
        goto bailout;
    }
    size = sizeof( clientFormat );
    result = ExtAudioFileSetProperty( destAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat );
    if( result != noErr )
    {
        DLog( @"Error while setting client format in destination file: %ld", result );
        goto bailout;
    }

    // Make a buffer
    bufferSizeInFrames = 8000;
    bufferSize = ( bufferSizeInFrames * sourceFormat.mBytesPerFrame );
    buffer = (UInt8 *)malloc( bufferSize );

    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame;
    bufferList.mBuffers[0].mData = buffer;
    bufferList.mBuffers[0].mDataByteSize = ( bufferSize );

    // Obtain total number of audio frames to encode
    size = sizeof( totalFrames );
    result = ExtAudioFileGetProperty( sourceAudioFile, kExtAudioFileProperty_FileLengthFrames, &size, &totalFrames );
    if( result != noErr )
    {
        DLog( @"Error in ExtAudioFileGetProperty, could not get kExtAudioFileProperty_FileLengthFrames from sourceFile: %ld", result );
        goto bailout;
    }

    encodedBytes = 0;
    totalBytes = totalFrames * sourceFormat.mBytesPerFrame;
    [threadDict setValue:[NSValue value:&totalBytes withObjCType:@encode(unsigned long long)] forKey:@"TotalBytes"];

    if (delegate != nil)
        [self performSelectorOnMainThread:@selector(didStartEncoding) withObject:nil waitUntilDone:NO];

    while( TRUE )
    {
        // Try to fill the buffer to capacity.
        UInt32 framesRead = bufferSizeInFrames;
        result = ExtAudioFileRead( sourceAudioFile, &framesRead, &bufferList );
        if( result != noErr )
        {
            DLog( @"Error in ExtAudioFileRead: %ld", result );
            success = NO;
            break;
        }

        // 0 frames read means EOF.
        if( framesRead == 0 ) {
            success = YES;
            break;
        }

        // Write.
        result = ExtAudioFileWrite( destAudioFile, framesRead, &bufferList );
        if( result != noErr )
        {
            DLog( @"Error in ExtAudioFileWrite: %ld", result );
            success = NO;
            break;
        }

        encodedBytes += framesRead * sourceFormat.mBytesPerFrame;

        if (delegate != nil)
            [self performSelectorOnMainThread:@selector(didEncodeBytes:) withObject:[NSValue value:&encodedBytes withObjCType:@encode(unsigned long long)] waitUntilDone:NO];

        if ([[NSThread currentThread] isCancelled]) {
            cancelled = YES;
            DLog( @"Encoding was cancelled." );
            success = NO;
            break;
        }
    }

    free( buffer );

    // Close the files.
    ExtAudioFileDispose( sourceAudioFile );
    ExtAudioFileDispose( destAudioFile );

bailout:
    encoderStatus.result = result;
    [threadDict setValue:[NSValue value:&encoderStatus withObjCType:@encode(RXAudioEncoderStatusType)] forKey:@"EncodingError"];

    // Report to the delegate if one exists
    if (delegate != nil)
        if (success)
            [self performSelectorOnMainThread:@selector(didEncodeFile) withObject:nil waitUntilDone:YES];
        else if (cancelled)
            [self performSelectorOnMainThread:@selector(encodingCancelled) withObject:nil waitUntilDone:YES];
        else
            [self performSelectorOnMainThread:@selector(failedToEncodeFile) withObject:nil waitUntilDone:YES];

    // Clear the partially encoded file if encoding failed or is cancelled midway
    if ((cancelled || !success) && [fileManager fileExistsAtPath:destPath])
        [fileManager removeItemAtURL:destURL error:NULL];

    [threadDict setValue:[NSNumber numberWithBool:NO] forKey:@"isEncoding"];

    [pool release];
}
于 2011-06-11T09:19:33.047 に答える
0

Sebastian's answer のコードを試してみましたが、圧縮されていないファイル (aif、wav、caf) では機能しましたが、非可逆圧縮ファイル (mp3) では機能しませんでした。のエラーコードもありました-50が、ExtAudioFileReadではなくExtAudioFileSetProperty. この質問から、このエラーは関数のパラメーターに問題があることを示していることがわかりました。オーディオ ファイルを読み取るためのバッファのサイズが 0 バイトであることが判明しました。これは、次の行の結果です。

int bufferSize = ( bufferSizeInFrames * sourceFormat.mBytesPerFrame );

clientFormat代わりにフレームあたりのバイト数を使用するように切り替えると(sourceFormatの値は0でした)、うまくいきました:

int bufferSize = ( bufferSizeInFrames * clientFormat.mBytesPerFrame );

この行は質問コードにもありましたが、それが問題ではなかったと思います (ただし、コメントするにはテキストが多すぎました)。

于 2012-12-25T02:03:54.553 に答える
0

サンプルレートが一致していますか? エラーが発生した時点での値を出力できますclientFormatか? outputFormatそれ以外の場合は、が必要になると思いますAudioConverter

于 2011-05-15T11:29:20.737 に答える