void set_stream() { OSStatus err; UInt32 propertySize; // get the magic cookie, if any, from the converter err = AudioQueueGetPropertySize(d_owner->queue, kAudioConverterCompressionMagicCookie, &propertySize); if (err == noErr && propertySize > 0) { // there is valid cookie data to be fetched; get it Byte *magicCookie = (Byte *)malloc(propertySize); err = AudioQueueGetProperty(d_owner->queue, kAudioConverterCompressionMagicCookie, magicCookie, &propertySize); if (err == 0) { // now set the magic cookie on the output file // even though some formats have cookies, some files don't take them, so we ignore the error /*err =*/ AudioFileSetProperty(d_owner->stm, kAudioFilePropertyMagicCookieData, propertySize, magicCookie); } free(magicCookie); } /* if (err != 0) trace_fmt("failed to set stream, %.4s", &err); */ }
// Some audio formats have a magic cookie associated with them which is required to decompress audio data // When converting audio, a magic cookie may be returned by the Audio Converter so that it may be stored along with // the output data -- This is done so that it may then be passed back to the Audio Converter at a later time as required static void WriteCookie(AudioConverterRef converter, AudioFileID destinationFileID) { // grab the cookie from the converter and write it to the destinateion file UInt32 cookieSize = 0; OSStatus error = AudioConverterGetPropertyInfo(converter, kAudioConverterCompressionMagicCookie, &cookieSize, NULL); // if there is an error here, then the format doesn't have a cookie - this is perfectly fine as some formats do not if (noErr == error && 0 != cookieSize) { char* cookie = new char [cookieSize]; error = AudioConverterGetProperty(converter, kAudioConverterCompressionMagicCookie, &cookieSize, cookie); if (noErr == error) { error = AudioFileSetProperty(destinationFileID, kAudioFilePropertyMagicCookieData, cookieSize, cookie); if (noErr == error) { printf("Writing magic cookie to destination file: %ld\n", cookieSize); } else { printf("Even though some formats have cookies, some files don't take them and that's OK\n"); } } else { printf("Could not Get kAudioConverterCompressionMagicCookie from Audio Converter!\n"); } delete [] cookie; } }
void MyCopyEncoderCookieToFile(AudioQueueRef queue, AudioFileID theFile) { OSStatus error; UInt32 propertySize; error = AudioQueueGetPropertySize(queue, kAudioConverterCompressionMagicCookie, &propertySize); if (error == noErr && propertySize > 0) { Byte *magicCookie = (Byte *)malloc(propertySize); CheckError(AudioQueueGetProperty(queue, kAudioQueueProperty_MagicCookie, magicCookie, &propertySize), "Couldn't get audio queue's magic cookie!"); CheckError(AudioFileSetProperty(theFile, kAudioFilePropertyMagicCookieData, propertySize, magicCookie), "Couldn't set audio file's magic cookie"); free(magicCookie); } }
// _______________________________________________________________________________________ // void CAAudioFile::FlushEncoder() { if (mConverter != NULL) { mFinishingEncoding = true; WritePacketsFromCallback(WriteInputProc, this); mFinishingEncoding = false; // get priming info from converter, set it on the file if (mFileDataFormat.mBitsPerChannel == 0) { UInt32 propertySize; OSStatus err; AudioConverterPrimeInfo primeInfo; propertySize = sizeof(primeInfo); err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo); if (err == noErr) { AudioFilePacketTableInfo pti; propertySize = sizeof(pti); err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); if (err == noErr) { //printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames; pti.mPrimingFrames = primeInfo.leadingFrames; pti.mRemainderFrames = primeInfo.trailingFrames; pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames; //printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file"); } } } } }
// ____________________________________________________________________________________ // Copy a queue's encoder's magic cookie to an audio file. static void MyCopyEncoderCookieToFile(AudioQueueRef theQueue, AudioFileID theFile) { OSStatus err; UInt32 propertySize; // get the magic cookie, if any, from the converter err = AudioQueueGetPropertySize(theQueue, kAudioConverterCompressionMagicCookie, &propertySize); if (err == noErr && propertySize > 0) { // there is valid cookie data to be fetched; get it Byte *magicCookie = (Byte *)malloc(propertySize); try { XThrowIfError(AudioQueueGetProperty(theQueue, kAudioConverterCompressionMagicCookie, magicCookie, &propertySize), "get audio converter's magic cookie"); // now set the magic cookie on the output file // even though some formats have cookies, some files don't take them, so we ignore the error /*err =*/ AudioFileSetProperty(theFile, kAudioFilePropertyMagicCookieData, propertySize, magicCookie); } catch (CAXException e) { char buf[256]; fprintf(stderr, "MyCopyEncoderCookieToFile: %s (%s)\n", e.mOperation, e.FormatError(buf)); } catch (...) { fprintf(stderr, "MyCopyEncoderCookieToFile: Unexpected exception\n"); } free(magicCookie); } }
OSStatus SetMagicCookieForFile ( AudioQueueRef inQueue, // 1 AudioFileID inFile // 2 ) { OSStatus result = noErr; // 3 UInt32 cookieSize; // 4 if ( AudioQueueGetPropertySize ( // 5 inQueue, kAudioQueueProperty_MagicCookie, &cookieSize ) == noErr ) { char* magicCookie = (char *) malloc (cookieSize); // 6 if ( AudioQueueGetProperty ( // 7 inQueue, kAudioQueueProperty_MagicCookie, magicCookie, &cookieSize ) == noErr ) result = AudioFileSetProperty ( // 8 inFile, kAudioFilePropertyMagicCookieData, cookieSize, magicCookie ); free (magicCookie); // 9 } return result; // 10 }
void HLAudioFile::SetAudioFrameSize(SInt64 inSize) { ThrowIf(mAudioFileID == 0, CAException(fnOpnErr), "HLAudioFile::SetAudioFrameSize: file isn't prepared"); UInt32 theSize = sizeof(UInt64); UInt64 theFrameSize = inSize; OSStatus theError = AudioFileSetProperty(mAudioFileID, kAudioFilePropertyAudioDataPacketCount, theSize, &theFrameSize); ThrowIfError(theError, CAException(theError), "HLAudioFile::SetAudioFrameSize: couldn't set the property"); }
// Sets the packet table containing information about the number of valid frames in a file and where they begin and end // for the file types that support this information. // Calling this function makes sure we write out the priming and remainder details to the destination file static void WritePacketTableInfo(AudioConverterRef converter, AudioFileID destinationFileID) { UInt32 isWritable; UInt32 dataSize; OSStatus error = AudioFileGetPropertyInfo(destinationFileID, kAudioFilePropertyPacketTableInfo, &dataSize, &isWritable); if (noErr == error && isWritable) { AudioConverterPrimeInfo primeInfo; dataSize = sizeof(primeInfo); // retrieve the leadingFrames and trailingFrames information from the converter, error = AudioConverterGetProperty(converter, kAudioConverterPrimeInfo, &dataSize, &primeInfo); if (noErr == error) { // we have some priming information to write out to the destination file /* The total number of packets in the file times the frames per packet (or counting each packet's frames individually for a variable frames per packet format) minus mPrimingFrames, minus mRemainderFrames, should equal mNumberValidFrames. */ AudioFilePacketTableInfo pti; dataSize = sizeof(pti); error = AudioFileGetProperty(destinationFileID, kAudioFilePropertyPacketTableInfo, &dataSize, &pti); if (noErr == error) { // there's priming to write out to the file UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames; // get the total number of frames from the output file printf("Total number of frames from output file: %lld\n", totalFrames); pti.mPrimingFrames = primeInfo.leadingFrames; pti.mRemainderFrames = primeInfo.trailingFrames; pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames; error = AudioFileSetProperty(destinationFileID, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti); if (noErr == error) { printf("Writing packet table information to destination file: %ld\n", sizeof(pti)); printf(" Total valid frames: %lld\n", pti.mNumberValidFrames); printf(" Priming frames: %ld\n", pti.mPrimingFrames); printf(" Remainder frames: %ld\n", pti.mRemainderFrames); } else { printf("Some audio files can't contain packet table information and that's OK\n"); } } else { printf("Getting kAudioFilePropertyPacketTableInfo error: %ld\n", error); } } else { printf("No kAudioConverterPrimeInfo available and that's OK\n"); } } else { printf("GetPropertyInfo for kAudioFilePropertyPacketTableInfo error: %ld, isWritable: %ld\n", error, isWritable); } }
void SoundRecorder::setAudioFileMagicCookie(void) { /* Query the size of the magic cookie: */ UInt32 magicCookieSize; if(AudioQueueGetPropertySize(queue,kAudioQueueProperty_MagicCookie,&magicCookieSize)==noErr) { /* Allocate a buffer for the magic cookie: */ char* magicCookie=new char[magicCookieSize]; /* Copy the magic cookie from the audio queue into the audio file: */ if(AudioQueueGetProperty(queue,kAudioQueueProperty_MagicCookie,magicCookie,&magicCookieSize)==noErr) AudioFileSetProperty(audioFile,kAudioFilePropertyMagicCookieData,magicCookieSize,magicCookie); /* Delete the cookie buffer: */ delete[] magicCookie; } }
// ____________________________________________________________________________________ // Copy a queue's encoder's magic cookie to an audio file. static void MyCopyEncoderCookieToFile(AudioQueueRef theQueue, AudioFileID theFile) { OSStatus err; UInt32 propertySize; // get the magic cookie, if any, from the converter err = AudioQueueGetPropertySize(theQueue, kAudioConverterCompressionMagicCookie, &propertySize); if (err == noErr && propertySize > 0) { // there is valid cookie data to be fetched; get it Byte *magicCookie = (Byte *)malloc(propertySize); CheckError(AudioQueueGetProperty(theQueue, kAudioConverterCompressionMagicCookie, magicCookie, &propertySize), "get audio converter's magic cookie"); // now set the magic cookie on the output file err = AudioFileSetProperty(theFile, kAudioFilePropertyMagicCookieData, propertySize, magicCookie); free(magicCookie); } }
// Write output channel layout to destination file static void WriteDestinationChannelLayout(AudioConverterRef converter, AudioFileID sourceFileID, AudioFileID destinationFileID) { UInt32 layoutSize = 0; bool layoutFromConverter = true; OSStatus error = AudioConverterGetPropertyInfo(converter, kAudioConverterOutputChannelLayout, &layoutSize, NULL); // if the Audio Converter doesn't have a layout see if the input file does if (error || 0 == layoutSize) { error = AudioFileGetPropertyInfo(sourceFileID, kAudioFilePropertyChannelLayout, &layoutSize, NULL); layoutFromConverter = false; } if (noErr == error && 0 != layoutSize) { char* layout = new char[layoutSize]; if (layoutFromConverter) { error = AudioConverterGetProperty(converter, kAudioConverterOutputChannelLayout, &layoutSize, layout); if (error) printf("Could not Get kAudioConverterOutputChannelLayout from Audio Converter!\n"); } else { error = AudioFileGetProperty(sourceFileID, kAudioFilePropertyChannelLayout, &layoutSize, layout); if (error) printf("Could not Get kAudioFilePropertyChannelLayout from source file!\n"); } if (noErr == error) { error = AudioFileSetProperty(destinationFileID, kAudioFilePropertyChannelLayout, layoutSize, layout); if (noErr == error) { printf("Writing channel layout to destination file: %ld\n", layoutSize); } else { printf("Even though some formats have layouts, some files don't take them and that's OK\n"); } } delete [] layout; } }
// _______________________________________________________________________________________ // void CAAudioFile::SetNumberFrames(SInt64 nFrames) { XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM"); XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file"); }
// _______________________________________________________________________________________ // // called to create the file -- or update its format/channel layout/properties based on an encoder // setting change void CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype) { LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this); XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared"); UInt32 propertySize; OSStatus err; AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat; #if VERBOSE_CONVERTER mFileDataFormat.PrintFormat(stdout, "", "Specified file data format"); #endif // Find out the actual format the converter will produce. This is necessary in // case the bitrate has forced a lower sample rate, which needs to be set correctly // in the stream description passed to AudioFileCreate. if (mConverter != NULL) { propertySize = sizeof(AudioStreamBasicDescription); Float64 origSampleRate = mFileDataFormat.mSampleRate; XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description"); // do the same for the channel layout being output by the converter #if VERBOSE_CONVERTER mFileDataFormat.PrintFormat(stdout, "", "Converter output"); #endif if (fiszero(mFileDataFormat.mSampleRate)) mFileDataFormat.mSampleRate = origSampleRate; err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL); if (err == noErr && propertySize > 0) { AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize)); err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout); if (err) { free(layout); XThrow(err, "couldn't get audio converter's output channel layout"); } mFileChannelLayout = layout; #if VERBOSE_CHANNELMAP printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); #endif free(layout); } } // create the output file if (mMode == kPreparingToCreate) { CAStreamBasicDescription newFileDataFormat = mFileDataFormat; if (fiszero(newFileDataFormat.mSampleRate)) newFileDataFormat.mSampleRate = 44100; // just make something up for now #if VERBOSE_CONVERTER newFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); #endif XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file"); mMode = kPreparingToWrite; mOwnOpenFile = true; } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) { // second check must be explicit since operator== on ASBD treats SR of zero as "don't care" if (fiszero(mFileDataFormat.mSampleRate)) mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate; #if VERBOSE_CONVERTER mFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); #endif XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0"); XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format"); } UInt32 deferSizeUpdates = 1; err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates); if (mConverter != NULL) { // encoder // get the magic cookie, if any, from the converter delete[] mMagicCookie; mMagicCookie = NULL; mMagicCookieSize = 0; err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL); // we can get a noErr result and also a propertySize == 0 // -- if the file format does support magic cookies, but this file doesn't have one. if (err == noErr && propertySize > 0) { mMagicCookie = new Byte[propertySize]; XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie"); mMagicCookieSize = propertySize; // the converter lies and tell us the wrong size // now set the magic cookie on the output file UInt32 willEatTheCookie = false; // the converter wants to give us one; will the file take it? err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, NULL, &willEatTheCookie); if (err == noErr && willEatTheCookie) { #if VERBOSE_CONVERTER printf("Setting cookie on encoded file\n"); #endif XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie"); } } // get maximum packet size propertySize = sizeof(UInt32); XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size"); AllocateBuffers(true /* okToFail */); } else { InitFileMaxPacketSize(); } if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) { // don't bother tagging mono/stereo files UInt32 isWritable; err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable); if (!err && isWritable) { #if VERBOSE_CHANNELMAP printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); #endif err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout, mFileChannelLayout.Size(), &mFileChannelLayout.Layout()); if (err) CAXException::Warning("could not set the file's channel layout", err); } else { #if VERBOSE_CHANNELMAP printf("file won't accept a channel layout (write)\n"); #endif } } UpdateClientMaxPacketSize(); // also sets mFrame0Offset mPacketMark = 0; mFrameMark = 0; }