int main (int argc, const char * argv[]) { char* filePath = NULL; bool overwrite = false; ComponentDescription compDesc = {0, 0, 0, 0, 0}; AudioFileID inputFileID = 0; AudioFileID outputFileID = 0; CAStreamBasicDescription desc; AudioUnit theUnit = 0; setbuf (stdout, NULL); for (int i = 1; i < argc; ++i) { if (strcmp (argv[i], "-u") == 0) { if ( (i + 3) < argc ) { compDesc.componentType = str2OSType (argv[i + 1]); compDesc.componentSubType = str2OSType (argv[i + 2]); compDesc.componentManufacturer = str2OSType (argv[i + 3]); Component comp = FindNextComponent (NULL, &compDesc); if (comp == NULL) break; OpenAComponent (comp, &theUnit); i += 3; } else { printf ("Which Component:\n%s", usageStr); return -1; } } else if (strcmp (argv[i], "-f") == 0) { filePath = const_cast<char*>(argv[++i]); printf ("Input File:%s\n", filePath); } else if (strcmp (argv[i], "-o") == 0) { overwrite = true; } else { printf ("%s\n", usageStr); return -1; } } if (compDesc.componentType == 0) { printf ("Must specify AU:\n%s\n", usageStr); return -1; } if (theUnit == 0) { printf ("Can't find specified unit\n"); return -1; } if (filePath == NULL) { printf ("Must specify file to process:\n%s\n", usageStr); return -1; } OSStatus result = 0; if (result = InputFile (filePath, inputFileID)) { printf ("Result = %ld, parsing input file, exit...\n", result); return result; } UInt32 fileType; UInt32 size = sizeof (fileType); result = AudioFileGetProperty (inputFileID, kAudioFilePropertyFileFormat, &size, &fileType); if (result) { printf ("Error getting File Type of input file:%ld, exit...\n", result); return result; } size = sizeof (desc); result = AudioFileGetProperty (inputFileID, kAudioFilePropertyDataFormat, &size, &desc); if (result) { printf ("Error getting File Format of input file:%ld, exit...\n", result); return result; } if (desc.IsPCM() == false) { printf ("Only processing linear PCM file types and data:\n"); desc.Print(); return -1; } result = OutputFile (filePath, fileType, compDesc.componentSubType, overwrite, desc, outputFileID); if (result) { printf ("Error creating output file:%ld, exit...\n", result); return result; } // at this point we're ready to process return Process (theUnit, compDesc, inputFileID, desc, outputFileID); }
void CAAudioFileConverter::GenerateOutputFileName(const char *inputFilePath, const CAStreamBasicDescription &inputFormat, const CAStreamBasicDescription &outputFormat, OSType outputFileType, char *outName) { struct stat sb; char inputDir[256]; char inputBasename[256]; strcpy(inputDir, dirname(inputFilePath)); const char *infname = basename(inputFilePath); const char *inext = strrchr(infname, '.'); if (inext == NULL) strcpy(inputBasename, infname); else { int n; memcpy(inputBasename, infname, n = inext - infname); inputBasename[n] = '\0'; } CFArrayRef exts; UInt32 propSize = sizeof(exts); XThrowIfError(AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, sizeof(OSType), &outputFileType, &propSize, &exts), "generate output file name"); char outputExt[32]; CFStringRef cfext = (CFStringRef)CFArrayGetValueAtIndex(exts, 0); CFStringGetCString(cfext, outputExt, sizeof(outputExt), kCFStringEncodingUTF8); CFRelease(exts); // 1. olddir + oldname + newext sprintf(outName, "%s/%s.%s", inputDir, inputBasename, outputExt); #if TARGET_OS_MAC if (lstat(outName, &sb)) return; #else if (stat(outName, &sb)) return; #endif if (outputFormat.IsPCM()) { // If sample rate changed: // 2. olddir + oldname + "-SR" + newext if (inputFormat.mSampleRate != outputFormat.mSampleRate && outputFormat.mSampleRate != 0.) { sprintf(outName, "%s/%s-%.0fk.%s", inputDir, inputBasename, outputFormat.mSampleRate/1000., outputExt); #if TARGET_OS_MAC if (lstat(outName, &sb)) return; #else if (stat(outName, &sb)) return; #endif } // If bit depth changed: // 3. olddir + oldname + "-bit" + newext if (inputFormat.mBitsPerChannel != outputFormat.mBitsPerChannel) { sprintf(outName, "%s/%s-%ldbit.%s", inputDir, inputBasename, outputFormat.mBitsPerChannel, outputExt); #if TARGET_OS_MAC if (lstat(outName, &sb)) return; #else if (stat(outName, &sb)) return; #endif } } // maybe more with channels/layouts? $$$ // now just append digits for (int i = 1; ; ++i) { sprintf(outName, "%s/%s-%d.%s", inputDir, inputBasename, i, outputExt); #if TARGET_OS_MAC if (lstat(outName, &sb)) return; #else if (stat(outName, &sb)) return; #endif } }
// _______________________________________________________________________________________ // void CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout) { LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this); XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file"); bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat); if (dataFormatChanging) { CloseConverter(); if (mWriteBufferList) { delete mWriteBufferList; mWriteBufferList = NULL; } mClientDataFormat = dataFormat; } if (layout && layout->IsValid()) { XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map"); mClientChannelLayout = *layout; } bool differentLayouts; if (mClientChannelLayout.IsValid()) { if (mFileChannelLayout.IsValid()) { differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag(); #if VERBOSE_CHANNELMAP printf("two valid layouts, %s\n", differentLayouts ? "different" : "same"); #endif } else { differentLayouts = false; #if VERBOSE_CHANNELMAP printf("valid client layout, unknown file layout\n"); #endif } } else { differentLayouts = false; #if VERBOSE_CHANNELMAP if (mFileChannelLayout.IsValid()) printf("valid file layout, unknown client layout\n"); else printf("two invalid layouts\n"); #endif } if (mClientDataFormat != mFileDataFormat || differentLayouts) { // We need an AudioConverter. if (mMode == kReading) { // file -> client (decode) //mFileDataFormat.PrintFormat( stdout, "", "File: "); //mClientDataFormat.PrintFormat(stdout, "", "Client: "); if (mConverter == NULL) XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter), "create audio converter"); #if VERBOSE_CONVERTER printf("CAAudioFile %p -- created converter\n", this); CAShow(mConverter); #endif // set the magic cookie, if any (for decode) if (mMagicCookie) SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM()); // we get cookies from some AIFF's but the converter barfs on them, // so we set canFail to true for PCM SetConverterChannelLayout(false, mFileChannelLayout); SetConverterChannelLayout(true, mClientChannelLayout); // propagate leading/trailing frame counts if (mFileDataFormat.mBitsPerChannel == 0) { UInt32 propertySize; OSStatus err; AudioFilePacketTableInfo pti; propertySize = sizeof(pti); err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) { AudioConverterPrimeInfo primeInfo; primeInfo.leadingFrames = pti.mPrimingFrames; primeInfo.trailingFrames = pti.mRemainderFrames; /* ignore any error. better to play it at all than not. */ /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo); //XThrowIfError(err, "couldn't set prime info on converter"); } } } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) { // client -> file (encode) if (mConverter == NULL) XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter"); mWriteBufferList = CABufferList::New("", mClientDataFormat); SetConverterChannelLayout(false, mClientChannelLayout); SetConverterChannelLayout(true, mFileChannelLayout); if (mMode == kPreparingToWrite) FileFormatChanged(); } else XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known"); } UpdateClientMaxPacketSize(); }
void CAAudioFileConverter::ConvertFile(const ConversionParameters &_params) { FSRef destFSRef; UInt32 propertySize; CAStreamBasicDescription destFormat; CAAudioChannelLayout origSrcFileLayout, srcFileLayout, destFileLayout; bool openedSourceFile = false, createdOutputFile = false; mParams = _params; mReadBuffer = NULL; mReadPtrs = NULL; CABufferList *writeBuffer = NULL; CABufferList *writePtrs = NULL; PrepareConversion(); try { if (TaggedDecodingFromCAF()) ReadCAFInfo(); OpenInputFile(); openedSourceFile = true; // get input file's format const CAStreamBasicDescription &srcFormat = mSrcFile.GetFileDataFormat(); if (mParams.flags & kOpt_Verbose) { printf("Input file: %s, %qd frames\n", mParams.input.filePath ? basename(mParams.input.filePath) : "?", mSrcFile.GetNumberFrames()); } mSrcFormat = srcFormat; bool encoding = !destFormat.IsPCM(); bool decoding = !srcFormat.IsPCM(); // prepare output file's format destFormat = mParams.output.dataFormat; if (!encoding && destFormat.mSampleRate == 0.) // on encode, it's OK to have a 0 sample rate; ExtAudioFile will get the SR from the converter and set it on the file. // on decode or PCM->PCM, a sample rate of 0 is interpreted as using the source sample rate destFormat.mSampleRate = srcFormat.mSampleRate; // source channel layout srcFileLayout = mSrcFile.GetFileChannelLayout(); origSrcFileLayout = srcFileLayout; if (mParams.input.channelLayoutTag != 0) { XThrowIf(AudioChannelLayoutTag_GetNumberOfChannels(mParams.input.channelLayoutTag) != srcFormat.mChannelsPerFrame, -1, "input channel layout has wrong number of channels for file"); srcFileLayout = CAAudioChannelLayout(mParams.input.channelLayoutTag); mSrcFile.SetFileChannelLayout(srcFileLayout); } // destination channel layout int outChannels = mParams.output.channels; if (mParams.output.channelLayoutTag != 0) { // use the one specified by caller, if any destFileLayout = CAAudioChannelLayout(mParams.output.channelLayoutTag); } else if (srcFileLayout.IsValid()) { // otherwise, assume the same as the source, if any destFileLayout = srcFileLayout; } if (destFileLayout.IsValid()) { // the output channel layout specifies the number of output channels if (outChannels != -1) XThrowIf((unsigned)outChannels != destFileLayout.NumberChannels(), -1, "output channel layout has wrong number of channels"); else outChannels = destFileLayout.NumberChannels(); } if (!(mParams.flags & kOpt_NoSanitizeOutputFormat)) { // adjust the output format's channels; output.channels overrides the channels if (outChannels == -1) outChannels = srcFormat.mChannelsPerFrame; if (outChannels > 0) { destFormat.mChannelsPerFrame = outChannels; destFormat.mBytesPerPacket *= outChannels; destFormat.mBytesPerFrame *= outChannels; } // use AudioFormat API to clean up the output format propertySize = sizeof(AudioStreamBasicDescription); XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &propertySize, &destFormat), "get destination format info"); } OpenOutputFile(srcFormat, destFormat, destFSRef, destFileLayout); createdOutputFile = true; mDestFormat = destFormat; // set up client formats CAStreamBasicDescription srcClientFormat, destClientFormat; { CAAudioChannelLayout srcClientLayout, destClientLayout; if (encoding) { if (decoding) { // transcoding // XThrowIf(encoding && decoding, -1, "transcoding not currently supported"); if (srcFormat.mChannelsPerFrame > 2 || destFormat.mChannelsPerFrame > 2) CAXException::Warning("Transcoding multichannel audio may not handle channel layouts correctly", 0); srcClientFormat.SetCanonical(std::min(srcFormat.mChannelsPerFrame, destFormat.mChannelsPerFrame), true); srcClientFormat.mSampleRate = std::max(srcFormat.mSampleRate, destFormat.mSampleRate); mSrcFile.SetClientFormat(srcClientFormat, NULL); destClientFormat = srcClientFormat; } else { // encoding srcClientFormat = srcFormat; destClientFormat = srcFormat; } // by here, destClientFormat will have a valid sample rate destClientLayout = srcFileLayout.IsValid() ? srcFileLayout : destFileLayout; mDestFile.SetClientFormat(destClientFormat, &destClientLayout); } else { // decoding or PCM->PCM if (destFormat.mSampleRate == 0.) destFormat.mSampleRate = srcFormat.mSampleRate; destClientFormat = destFormat; srcClientFormat = destFormat; srcClientLayout = destFileLayout; mSrcFile.SetClientFormat(srcClientFormat, &srcClientLayout); } } XThrowIf(srcClientFormat.mBytesPerPacket == 0, -1, "source client format not PCM"); XThrowIf(destClientFormat.mBytesPerPacket == 0, -1, "dest client format not PCM"); if (encoding) { // set the bitrate if (mParams.output.bitRate != -1) { if (mParams.flags & kOpt_Verbose) printf("bitrate = %ld\n", mParams.output.bitRate); mDestFile.SetConverterProperty(kAudioConverterEncodeBitRate, sizeof(UInt32), &mParams.output.bitRate); } // set the codec quality if (mParams.output.codecQuality != -1) { if (mParams.flags & kOpt_Verbose) printf("codec quality = %ld\n", mParams.output.codecQuality); mDestFile.SetConverterProperty(kAudioConverterCodecQuality, sizeof(UInt32), &mParams.output.codecQuality); } // set the bitrate strategy -- called bitrate format in the codecs since it had already shipped if (mParams.output.strategy != -1) { if (mParams.flags & kOpt_Verbose) printf("strategy = %ld\n", mParams.output.strategy); mDestFile.SetConverterProperty(kAudioCodecBitRateFormat, sizeof(UInt32), &mParams.output.strategy); } } // set the SRC quality if (mParams.output.srcQuality != -1) { if (srcFormat.mSampleRate != 0. && destFormat.mSampleRate != 0. && srcFormat.mSampleRate != destFormat.mSampleRate) { if (mParams.flags & kOpt_Verbose) printf("SRC quality = %ld\n", mParams.output.srcQuality); if (encoding) mDestFile.SetConverterProperty(kAudioConverterSampleRateConverterQuality, sizeof(UInt32), &mParams.output.srcQuality); else mSrcFile.SetConverterProperty(kAudioConverterSampleRateConverterQuality, sizeof(UInt32), &mParams.output.srcQuality); } } if (decoding) { if (mParams.output.primeMethod != -1) mSrcFile.SetConverterProperty(kAudioConverterPrimeMethod, sizeof(UInt32), &mParams.output.primeMethod); } PrintFormats(&origSrcFileLayout); // prepare I/O buffers UInt32 bytesToRead = 0x10000; UInt32 framesToRead = bytesToRead; // OK, ReadPackets will limit as appropriate ComputeReadSize(srcFormat, destFormat, bytesToRead, framesToRead); // const SInt64 totalFrames = mSrcFile.GetNumberFrames(); //#warning "GetNumberFrames() can be prohibitively slow for some formats" mReadBuffer = CABufferList::New("readbuf", srcClientFormat); mReadBuffer->AllocateBuffers(bytesToRead); mReadPtrs = CABufferList::New("readptrs", srcClientFormat); BeginConversion(); while (true) { //XThrowIf(Progress(mSrcFile.Tell(), totalFrames), userCanceledErr, "user stopped"); // this was commented out for awhile -- performance? make it optional? UInt32 nFrames = framesToRead; mReadPtrs->SetFrom(mReadBuffer); AudioBufferList *readbuf = &mReadPtrs->GetModifiableBufferList(); mSrcFile.Read(nFrames, readbuf); //printf("read %ld of %ld frames\n", nFrames, framesToRead); if (nFrames == 0) break; mDestFile.Write(nFrames, readbuf); if (ShouldTerminateConversion()) break; } if (decoding) { // fix up the destination file's length if necessary and possible SInt64 nframes = mSrcFile.GetNumberFrames(); if (nframes != 0) { // only shorten, don't try to lengthen nframes = SInt64(ceil(nframes * destFormat.mSampleRate / srcFormat.mSampleRate)); if (nframes < mDestFile.GetNumberFrames()) { mDestFile.SetNumberFrames(nframes); } } } EndConversion(); } catch (...) { delete mReadBuffer; delete mReadPtrs; delete writeBuffer; delete writePtrs; if (!createdOutputFile) PrintFormats(&origSrcFileLayout); try { mSrcFile.Close(); } catch (...) { } try { mDestFile.Close(); } catch (...) { } if (createdOutputFile) unlink(mOutName); throw; } delete mReadBuffer; delete mReadPtrs; delete writeBuffer; delete writePtrs; mSrcFile.Close(); mDestFile.Close(); if (TaggedEncodingToCAF()) WriteCAFInfo(); if (mParams.flags & kOpt_Verbose) { // must close to flush encoder; GetNumberFrames() not necessarily valid until afterwards but then // the file is closed CAAudioFile temp; FSRef destFSRef; if (FSPathMakeRef((UInt8 *)mOutName, &destFSRef, NULL) == noErr) { temp.Open(destFSRef); printf("Output file: %s, %qd frames\n", basename(mOutName), temp.GetNumberFrames()); } } }
int main(int argc, const char *argv[]) { const char *recordFileName = NULL; // set up defaults AudioFileTypeID filetype = kAudioFileAIFFType; bool gotOutDataFormat = false; CAStreamBasicDescription dataFormat; dataFormat.mSampleRate = 44100.; // later get this from the hardware dataFormat.mFormatID = kAudioFormatLinearPCM; dataFormat.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; dataFormat.mFramesPerPacket = 1; dataFormat.mChannelsPerFrame = 2; dataFormat.mBitsPerChannel = 16; dataFormat.mBytesPerPacket = dataFormat.mBytesPerFrame = 4; SInt32 bitrate = -1, quality = -1; // parse arguments for (int i = 1; i < argc; ++i) { const char *arg = argv[i]; if (arg[0] != '-') { if (recordFileName != NULL) { fprintf(stderr, "may only specify one record file\n"); usage(); } recordFileName = arg; } else { arg += 1; if (arg[0] == 'f' || !strcmp(arg, "-file")) { if (++i == argc) MissingArgument(); filetype = Parse4CharCode(argv[i], "-f | --file"); } else if (arg[0] == 'd' || !strcmp(arg, "-data")) { if (++i == argc) MissingArgument(); if (!ParseStreamDescription(argv[i], dataFormat)) usage(); gotOutDataFormat = true; } else if (arg[0] == 'b' || !strcmp(arg, "-bitrate")) { if (++i == argc) MissingArgument(); bitrate = ParseInt(argv[i], "-b | --bitrate"); } else if (arg[0] == 'q' || !strcmp(arg, "-quality")) { if (++i == argc) MissingArgument(); quality = ParseInt(argv[i], "-q | --quality"); } else { fprintf(stderr, "unknown argument: %s\n\n", arg - 1); usage(); } } } if (recordFileName == NULL) usage(); if (!gotOutDataFormat) { if (filetype == 0) { fprintf(stderr, "no output file or data format specified\n\n"); usage(); } if (!CAAudioFileFormats::Instance()->InferDataFormatFromFileFormat(filetype, dataFormat)) { fprintf(stderr, "Couldn't infer data format from file format\n\n"); usage(); } } else if (filetype == 0) { if (!CAAudioFileFormats::Instance()->InferFileFormatFromDataFormat(dataFormat, filetype)) { dataFormat.PrintFormat(stderr, "", "Couldn't infer file format from data format"); usage(); } } unlink(recordFileName); if (dataFormat.IsPCM()) dataFormat.ChangeNumberChannels(2, true); else dataFormat.mChannelsPerFrame = 2; try { const int kNumberBuffers = 3; const unsigned kBufferSize = 0x8000; CAAudioFileRecorder recorder(kNumberBuffers, kBufferSize); FSRef parentDir; CFStringRef filename; XThrowIfError(PosixPathToParentFSRefAndName(recordFileName, parentDir, filename), "couldn't find output directory"); recorder.SetFile(parentDir, filename, filetype, dataFormat, NULL); CAAudioFile &recfile = recorder.GetFile(); if (bitrate >= 0) recfile.SetConverterProperty(kAudioConverterEncodeBitRate, sizeof(UInt32), &bitrate); if (quality >= 0) recfile.SetConverterProperty(kAudioConverterCodecQuality, sizeof(UInt32), &quality); Record(recorder); } catch (CAXException &e) { char buf[256]; fprintf(stderr, "Error: %s (%s)\n", e.mOperation, CAXException::FormatError(buf, e.mError)); return 1; } catch (...) { fprintf(stderr, "An unknown error occurred\n"); return 1; } return 0; }