HLFileSystemObject* HLAudioFileFactory::Create(const FSRef& inParentFSRef, CFStringRef inName, const void* inData, UInt32 inDataSize) { HLFile* theFile = NULL; // the data for an HLAudioFile is a stream descption ThrowIf(inDataSize != sizeof(AudioStreamBasicDescription), CAException(paramErr), "HLAudioFileFactory::Create: the data is supposed to be an AudioStreamBasicDescription"); const AudioStreamBasicDescription* theFormat = reinterpret_cast<const AudioStreamBasicDescription*>(inData); // create the file on disk FSRef theFSRef; AudioFileID theAudioFileID = 0; OSStatus theError = AudioFileCreate(&inParentFSRef, inName, mObjectType, theFormat, 0, &theFSRef, &theAudioFileID); if(theError == 0) { AudioFileClose(theAudioFileID); // make the object theFile = CreateObject(theFSRef); theFile->Prepare(); } else { ThrowIfError(theError, CAException(theError), "HLAudioFileFactory::Create: AudioFileCreate failed"); } return theFile; }
OSStatus OutputFile (const char *inputFilename, UInt32 inFileType, OSType subType, bool overwrite, const AudioStreamBasicDescription &desc, AudioFileID &outFile) { int len = strlen (inputFilename); char* copyStr = (char*)malloc (len); strcpy (copyStr, inputFilename); int i = len; FSRef parentDir; char filename[512]; memset (filename, 0, 512); OSStatus result = fnfErr; CFStringRef cfstr = NULL; while (--i >= 0) { if (copyStr[i] == '/') {// find the delimiter for the directory copyStr[i] = 0; require_noerr (result = FSPathMakeRef ((const UInt8*)copyStr, &parentDir, 0), home); int j = len; while (--j > i) { //find the ext if (inputFilename[j] == '.') break; } // copy the name itself first memcpy (filename, inputFilename + i + 1, (len - i - (len - j + 1))); // append the -subType suffix before the .ext char* temp = filename + strlen(filename); temp += sprintf (temp, "-%4.4s", (char*)&subType); // append the file type .ext memcpy (temp, inputFilename + j, (len - j)); printf ("Output File: Dir:%s, FileName:%s\n", copyStr, filename); cfstr = CFStringCreateWithCString (NULL, filename, kCFStringEncodingASCII); FSRef outRef; result = AudioFileCreate (&parentDir, cfstr, inFileType, &desc, 0/*flags*/, &outRef, &outFile); if (result == dupFNErr) { // file already exits - initialie it? if (overwrite) { len += 5; char* name = (char*)malloc (len); strcpy (name, copyStr); strcat (name, "/"); strcat (name, filename); require_noerr (result = FSPathMakeRef ((const UInt8*)name, &outRef, 0), home); require_noerr (result = AudioFileInitialize (&outRef, inFileType, &desc, 0, &outFile), home); free (name); } else { printf ("Output File exists - specify overwrite\n"); } } break; } } home: if (cfstr) CFRelease (cfstr); free (copyStr); return result; }
// _______________________________________________________________________________________ // // 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; }