Audiokonverter - AIFF zu mp3 mit AudioToolbox-Framework

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Audiokonverter - AIFF zu mp3 mit AudioToolbox-Framework

    Hallo liebe Entwicklergemeinde,

    ich befasse mich gerade mit dem Thema "Konvertierung einer Audio-Datei mit dem AudioToolbox-Framework".

    Ziel soll es sein eine aiff-Audiodatei in eine mp3-Audiodatei zu konvertieren.

    Um dies zu erreichen habe ich bereits eine Methode geschrieben die zumindest eine aiff-Audiodatei in eine aiff-Audiodatei "konvertieren" kann.
    Die AudioStreamBasicDescription ist dann in beiden Fällen gleich.

    Um eine mp3.Audiodatei erhalten zu können benötige ich folgende Informationen:

    File: howarddean.mp3
    File type ID: MPG3
    Data format: 2 ch, 44100 Hz, '.mp3' (0x00000000) 0 bits/channel, 0 bytes/packet, 1152 frames/packet, 0 bytes/frame
    no channel layout.
    estimated duration: 22.000 sec
    audio bytes: 357901
    audio packets: 856
    bit rate: 128000 bits per second
    packet size upper bound: 1052
    maximum packet size: 418
    audio data file offset: 499
    optimized

    Nähere Erklärungen:

    0 bytes per frame/packet means that each "packet" of audio data can have
    a different size. A packet in this case is a frame of MP3 data, which
    is a variable bit rate codec (the packets that it produces are of
    different sizes).
    Its called a vbr codec because each packet
    represents the same length of time - 1152 samples worth. So while the
    time represented in a packet of data remains constant, the number of
    bytes used to encode that amount of time will vary. This is the most
    common way that audio encoders work. Some common things you will see
    here are:
    Number of frames in each packet:MP3 - 1152 ALAC (Apple Lossless) - 4096AAC - 1024

    The
    bits / channel being zero is true for nearly all compressed
    representations of audio. Once you have encoded the audio data in this
    manner, it really doesn't have a notion of a bit depth anymore. The bit
    depth it will be decoded to is often floating point or otherwise
    dictated by the hardware the data is played back on.
    With
    uncompressed data, there is a direct representation of the audio data by
    some number of bits for each channel of data in the file. 16 or 24 bits
    are common for linear pcm for example.

    Daraus leite ich folgende AudioStreamBasicDescription für das output-Format ab:

    PHP-Quellcode

    1. - (void) setOutputStreamDesc;
    2. {
    3. memset(&outputStreamDesc, 0, sizeof( AudioStreamBasicDescription ) );
    4. // OutputFormat
    5. outputStreamDesc.mSampleRate = inputStreamDesc.mSampleRate;
    6. outputStreamDesc.mFormatID = kAudioFormatMPEGLayer3;
    7. outputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    8. outputStreamDesc.mChannelsPerFrame = inputStreamDesc.mChannelsPerFrame;
    9. outputStreamDesc.mFramesPerPacket = 1152;
    10. outputStreamDesc.mBitsPerChannel = 0;
    11. outputStreamDesc.mBytesPerPacket = 0;
    12. outputStreamDesc.mBytesPerFrame = 0;
    13. }
    Alles anzeigen


    Folgende Variablendeklarationen lege ich fest:

    aiffFile = @"./input.aif";
    mp3File = @"./output.mp3";
    bitrate = 64000;

    Somit erhalte ich folgenden Methodenaufruf:

    PHP-Quellcode

    1. [self convertFile:[self getURLRef:[self getInputPath:_originalDictateFileString]]
    2. inputFormat:inputClientFormatDesc
    3. outputFileURL:[self getURLRef:[self getOutputPath:_highCompressedOutputFileString]]
    4. outputFileType:kAudioFileMP3Type
    5. outputFormat:ouputClientFormatDesc
    6. outputBitRate:bitrate];


    Aufzurufende Methode:

    PHP-Quellcode

    1. const UInt32 kSrcBufSize = 32768;
    2. - (int) convertFile:(CFURLRef*)_inputFileURL
    3. inputFormat:(AudioStreamBasicDescription)_inputFormat
    4. outputFileURL:(CFURLRef*)_outputFileURL
    5. outputFileType:(AudioFileTypeID)_outputFileType
    6. outputFormat:(AudioStreamBasicDescription)_outputFormat
    7. outputBitRate:(UInt32)_outputBitRate;
    8. {
    9. ExtAudioFileRef infile, outfile;
    10. // first open the input file
    11. OSStatus err = ExtAudioFileOpenURL (_inputFileURL, &infile);
    12. [self throwIfOSStatusIsErr:err
    13. fromFunction:@"ExtAudioFileOpen"];
    14. // if outputBitRate is specified, this can change the sample rate of the output file
    15. // so we let this "take care of itself"
    16. if (_outputBitRate)
    17. _outputFormat.mSampleRate = 0.;
    18. // create the output file (this will erase an exsiting file)
    19. err = ExtAudioFileCreateWithURL (_outputFileURL, _outputFileType, &_outputFormat, NULL, kAudioFileFlags_EraseFile, &outfile);
    20. //XThrowIfError (err, "ExtAudioFileCreateNew");
    21. [self throwIfOSStatusIsErr:err
    22. fromFunction:@"ExtAudioFileCreateNew"];
    23. // get and set the client format - it should be lpcm
    24. AudioStreamBasicDescription clientFormat = (_inputFormat.mFormatID == kAudioFormatLinearPCM ? _inputFormat : _outputFormat);
    25. UInt32 size = sizeof(clientFormat);
    26. err = ExtAudioFileSetProperty(infile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
    27. //XThrowIfError (err, "ExtAudioFileSetProperty inFile, kExtAudioFileProperty_ClientDataFormat");
    28. [self throwIfOSStatusIsErr:err
    29. fromFunction:@"ExtAudioFileSetProperty inFile, kExtAudioFileProperty_ClientDataFormat"];
    30. size = sizeof(clientFormat);
    31. err = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
    32. //XThrowIfError (err, "ExtAudioFileSetProperty outFile, kExtAudioFileProperty_ClientDataFormat");
    33. [self throwIfOSStatusIsErr:err
    34. fromFunction:@"ExtAudioFileSetProperty outFile, kExtAudioFileProperty_ClientDataFormat"];
    35. if( _outputBitRate > 0 ) {
    36. printf ("Dest bit rate: %d\n", (int)_outputBitRate);
    37. AudioConverterRef outConverter;
    38. size = sizeof(outConverter);
    39. err = ExtAudioFileGetProperty(outfile, kExtAudioFileProperty_AudioConverter, &size, &outConverter);
    40. //XThrowIfError (err, "ExtAudioFileGetProperty outFile, kExtAudioFileProperty_AudioConverter");
    41. [self throwIfOSStatusIsErr:err
    42. fromFunction:@"ExtAudioFileGetProperty outFile, kExtAudioFileProperty_AudioConverter"];
    43. err = AudioConverterSetProperty(outConverter, kAudioConverterEncodeBitRate,
    44. sizeof(_outputBitRate), _outputBitRate);
    45. //XThrowIfError (err, "AudioConverterSetProperty, kAudioConverterEncodeBitRate");
    46. [self throwIfOSStatusIsErr:err
    47. fromFunction:@"AudioConverterSetProperty, kAudioConverterEncodeBitRate"];
    48. // we have changed the converter, so we should do this in case
    49. // setting a converter property changes the converter used by ExtAF in some manner
    50. CFArrayRef config = NULL;
    51. err = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ConverterConfig, sizeof(config), &config);
    52. //XThrowIfError (err, "ExtAudioFileSetProperty outFile, kExtAudioFileProperty_ConverterConfig");
    53. [self throwIfOSStatusIsErr:err
    54. fromFunction:@"ExtAudioFileSetProperty outFile, kExtAudioFileProperty_ConverterConfig"];
    55. }
    56. // set up buffers
    57. char srcBuffer[kSrcBufSize];
    58. // do the read and write - the conversion is done on and by the write call
    59. while (1)
    60. {
    61. AudioBufferList fillBufList;
    62. fillBufList.mNumberBuffers = 1;
    63. fillBufList.mBuffers[0].mNumberChannels = _inputFormat.mChannelsPerFrame;
    64. fillBufList.mBuffers[0].mDataByteSize = kSrcBufSize;
    65. fillBufList.mBuffers[0].mData = srcBuffer;
    66. // client format is always linear PCM - so here we determine how many frames of lpcm
    67. // we can read/write given our buffer size
    68. UInt32 numFrames = (kSrcBufSize / clientFormat.mBytesPerFrame);
    69. // printf("test %d\n", numFrames);
    70. err = ExtAudioFileRead (infile, &numFrames, &fillBufList);
    71. //XThrowIfError (err, "ExtAudioFileRead");
    72. [self throwIfOSStatusIsErr:err
    73. fromFunction:@"ExtAudioFileRead"];
    74. if (!numFrames) {
    75. // this is our termination condition
    76. break;
    77. }
    78. err = ExtAudioFileWrite(outfile, numFrames, &fillBufList);
    79. //XThrowIfError (err, "ExtAudioFileWrite");
    80. [self throwIfOSStatusIsErr:err
    81. fromFunction:@"ExtAudioFileWrite"];
    82. }
    83. // close
    84. ExtAudioFileDispose(outfile);
    85. ExtAudioFileDispose(infile);
    86. return 0;
    87. }
    Alles anzeigen


    Wenn ich den Code ausführe erhalte ich folgende Fehlermeldung:

    PHP-Quellcode

    1. *** Terminating app due to uncaught exception 'ConverterViewController exception', reason: 'Error in ExtAudioFileSetProperty outFile, kExtAudioFileProperty_ClientDataFormat, (OSStatus 1718449215)'


    Die Exception wird von folgender Methode geworfen:

    PHP-Quellcode

    1. size = sizeof(clientFormat);
    2. err = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
    3. //XThrowIfError (err, "ExtAudioFileSetProperty outFile, kExtAudioFileProperty_ClientDataFormat");
    4. [self throwIfOSStatusIsErr:err
    5. fromFunction:@"ExtAudioFileSetProperty outFile, kExtAudioFileProperty_ClientDataFormat"];


    Wenn ich die Methode nutze um eine aiff-Audiodatei in eine aiff-Audiodatei zu "konvertieren" funktioniert das ohne Probleme - die AudioStreamBasicDescription ist in beiden Fällen identisch.

    Wenn ich nun eine mp3-Audiodatei erhalten möchte und die AudioStreamBasicDescription entsprechen für das outputFileformat anpasse, so erhalte ich diese Exception.

    Ich habe bereits viel Zeit mit der Internetrecherche verbracht und konnte zwar mit Hilfe dieser Recherche das Problem etwas umrahmen aber nicht den tatsächlichen Kern des Problemes erkennen.
    In vielen Internetportalen wird dieses Problem zwar besprochen aber nicht sauber abgeschlossen.

    Ich würde mich sehr freuen wenn wir dieses Thema gemeinsam besprechen um eine saubere Lösung für die Nachwelt zu erhalten.

    Ich freue mich schon sehr auf die nachfolgende Diskussionsrunde mit euch!!! ;)

    Schönen Gruss, OsnaTiger