inline OSStatus SetInputCallback (CAAudioUnit &inUnit, AURenderCallbackStruct &inInputCallback) { return inUnit.SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inInputCallback, sizeof(inInputCallback)); }
OSStatus Preroll (CAAudioUnit & inAU, UInt32 inFrameSize) { CAStreamBasicDescription desc; OSStatus result = inAU.GetFormat (kAudioUnitScope_Input, 0, desc); bool hasInput = false; //we have input if (result == noErr) { sRenderCallback.inputProc = PrerollRenderProc; sRenderCallback.inputProcRefCon = 0; result = inAU.SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &sRenderCallback, sizeof(sRenderCallback)); if (result) return result; hasInput = true; } AudioUnitRenderActionFlags flags = 0; AudioTimeStamp time; memset (&time, 0, sizeof(time)); time.mFlags = kAudioTimeStampSampleTimeValid; CAStreamBasicDescription outputFormat; ca_require_noerr (result = inAU.GetFormat (kAudioUnitScope_Output, 0, outputFormat), home); { AUOutputBL list (outputFormat, inFrameSize); list.Prepare (); result = inAU.Render (&flags, &time, 0, inFrameSize, list.ABL()); if (result) { printf("A result %d\n", (int)result); goto home; } } home: if (hasInput) { // remove our installed callback sRenderCallback.inputProc = 0; sRenderCallback.inputProcRefCon = 0; inAU.SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &sRenderCallback, sizeof(sRenderCallback)); } return result; }
CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope) :mChans(NULL), mNumEls(0), mDidAllocate(false) { UInt32 elCount; if (inAU.GetElementCount (inScope, elCount)) return; if (elCount > 8) { mChans = new UInt32[elCount]; mDidAllocate = true; memset (mChans, 0, sizeof(int) * elCount); } else { mChans = mStaticChans; memset (mChans, 0, sizeof(int) * 8); } for (unsigned int i = 0; i < elCount; ++i) { UInt32 numChans; if (inAU.NumberChannels (inScope, i, numChans)) return; mChans[i] = numChans; } mNumEls = elCount; }
double PrepareFileAU (CAAudioUnit &au, CAStreamBasicDescription &fileFormat, AudioFileID audioFile) { // // calculate the duration UInt64 nPackets; UInt32 propsize = sizeof(nPackets); XThrowIfError (AudioFileGetProperty(audioFile, kAudioFilePropertyAudioDataPacketCount, &propsize, &nPackets), "kAudioFilePropertyAudioDataPacketCount"); Float64 fileDuration = (nPackets * fileFormat.mFramesPerPacket) / fileFormat.mSampleRate; ScheduledAudioFileRegion rgn; memset (&rgn.mTimeStamp, 0, sizeof(rgn.mTimeStamp)); rgn.mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; rgn.mTimeStamp.mSampleTime = 0; rgn.mCompletionProc = NULL; rgn.mCompletionProcUserData = NULL; rgn.mAudioFile = audioFile; rgn.mLoopCount = 1; rgn.mStartFrame = 0; rgn.mFramesToPlay = UInt32(nPackets * fileFormat.mFramesPerPacket); // tell the file player AU to play all of the file XThrowIfError (au.SetProperty (kAudioUnitProperty_ScheduledFileRegion, kAudioUnitScope_Global, 0,&rgn, sizeof(rgn)), "kAudioUnitProperty_ScheduledFileRegion"); // prime the fp AU with default values UInt32 defaultVal = 0; XThrowIfError (au.SetProperty (kAudioUnitProperty_ScheduledFilePrime, kAudioUnitScope_Global, 0, &defaultVal, sizeof(defaultVal)), "kAudioUnitProperty_ScheduledFilePrime"); // tell the fp AU when to start playing (this ts is in the AU's render time stamps; -1 means next render cycle) AudioTimeStamp startTime; memset (&startTime, 0, sizeof(startTime)); startTime.mFlags = kAudioTimeStampSampleTimeValid; startTime.mSampleTime = -1; XThrowIfError (au.SetProperty(kAudioUnitProperty_ScheduleStartTimeStamp, kAudioUnitScope_Global, 0, &startTime, sizeof(startTime)), "kAudioUnitProperty_ScheduleStartTimeStamp"); return fileDuration; }
void CAAUMIDIMap::SaveAsMapPList (AudioUnit inUnit, const AUParameterMIDIMapping* inMappings, UInt32 inNumMappings, CFPropertyListRef &outData, CFStringRef inName) { CACFDictionary mappingDict (false); CACFArray maps (true); for (UInt32 i = 0; i< inNumMappings; ++i) { CFPropertyListRef data; CAAUMIDIMap paramMap(inMappings[i]); paramMap.Save (data); if (data) { maps.AppendCFType (data); CFRelease(data); } } if (maps.GetNumberItems()) { mappingDict.AddCFType (kParamMIDIStr, maps.GetCFArray()); // Add the AU info here - where this map came from CAAudioUnit au (inUnit); CFPropertyListRef data; au.Comp().Save (&data); mappingDict.AddCFType (kAUStr, data); CFRelease(data); if (!inName) inName = CFSTR("Untitled"); mappingDict.AddString (CFSTR("name"), inName); mappingDict.AddUInt32 (CFSTR("version"), 1); outData = mappingDict.AsPropertyList(); } else { mappingDict.ShouldRelease(true); outData = NULL; } }
void MakeSimpleGraph (AUGraph &theGraph, CAAudioUnit &fileAU, CAStreamBasicDescription &fileFormat, AudioFileID audioFile) { XThrowIfError (NewAUGraph (&theGraph), "NewAUGraph"); CAComponentDescription cd; // output node cd.componentType = kAudioUnitType_Output; cd.componentSubType = kAudioUnitSubType_DefaultOutput; cd.componentManufacturer = kAudioUnitManufacturer_Apple; AUNode outputNode; XThrowIfError (AUGraphAddNode (theGraph, &cd, &outputNode), "AUGraphAddNode"); // file AU node AUNode fileNode; cd.componentType = kAudioUnitType_Generator; cd.componentSubType = kAudioUnitSubType_AudioFilePlayer; XThrowIfError (AUGraphAddNode (theGraph, &cd, &fileNode), "AUGraphAddNode"); // connect & setup XThrowIfError (AUGraphOpen (theGraph), "AUGraphOpen"); // install overload listener to detect when something is wrong AudioUnit anAU; XThrowIfError (AUGraphNodeInfo(theGraph, fileNode, NULL, &anAU), "AUGraphNodeInfo"); fileAU = CAAudioUnit (fileNode, anAU); // prepare the file AU for playback // set its output channels XThrowIfError (fileAU.SetNumberChannels (kAudioUnitScope_Output, 0, fileFormat.NumberChannels()), "SetNumberChannels"); // set the output sample rate of the file AU to be the same as the file: XThrowIfError (fileAU.SetSampleRate (kAudioUnitScope_Output, 0, fileFormat.mSampleRate), "SetSampleRate"); // load in the file XThrowIfError (fileAU.SetProperty(kAudioUnitProperty_ScheduledFileIDs, kAudioUnitScope_Global, 0, &audioFile, sizeof(audioFile)), "SetScheduleFile"); XThrowIfError (AUGraphConnectNodeInput (theGraph, fileNode, 0, outputNode, 0), "AUGraphConnectNodeInput"); // AT this point we make sure we have the file player AU initialized // this also propogates the output format of the AU to the output unit XThrowIfError (AUGraphInitialize (theGraph), "AUGraphInitialize"); // workaround a race condition in the file player AU usleep (10 * 1000); // if we have a surround file, then we should try to tell the output AU what the order of the channels will be if (fileFormat.NumberChannels() > 2) { UInt32 layoutSize = 0; OSStatus err; XThrowIfError (err = AudioFileGetPropertyInfo (audioFile, kAudioFilePropertyChannelLayout, &layoutSize, NULL), "kAudioFilePropertyChannelLayout"); if (!err && layoutSize) { char* layout = new char[layoutSize]; err = AudioFileGetProperty(audioFile, kAudioFilePropertyChannelLayout, &layoutSize, layout); XThrowIfError (err, "Get Layout From AudioFile"); // ok, now get the output AU and set its layout XThrowIfError (AUGraphNodeInfo(theGraph, outputNode, NULL, &anAU), "AUGraphNodeInfo"); err = AudioUnitSetProperty (anAU, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, layout, layoutSize); XThrowIfError (err, "kAudioUnitProperty_AudioChannelLayout"); delete [] layout; } } }