/* we're looking for the sequence output audiounit. */ static OSStatus GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit) { AUGraph graph; UInt32 nodecount, i; OSStatus err; err = MusicSequenceGetAUGraph(sequence, &graph); if (err != noErr) return err; err = AUGraphGetNodeCount(graph, &nodecount); if (err != noErr) return err; for (i = 0; i < nodecount; i++) { AUNode node; if (AUGraphGetIndNode(graph, i, &node) != noErr) continue; /* better luck next time. */ #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 /* this is deprecated, but works back to 10.0 */ { struct ComponentDescription desc; UInt32 classdatasize = 0; void *classdata = NULL; err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize, &classdata, aunit); if (err != noErr) continue; else if (desc.componentType != kAudioUnitType_Output) continue; else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) continue; } #else /* not deprecated, but requires 10.5 or later */ { # if !defined(AUDIO_UNIT_VERSION) || ((AUDIO_UNIT_VERSION + 0) < 1060) /* AUGraphAddNode () is changed to take an AudioComponentDescription* * desc parameter instead of a ComponentDescription* in the 10.6 SDK. * AudioComponentDescription is in 10.6 or newer, but it is actually * the same as struct ComponentDescription with 20 bytes of size and * the same offsets of all members, therefore, is binary compatible. */ # define AudioComponentDescription ComponentDescription # endif AudioComponentDescription desc; if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr) continue; else if (desc.componentType != kAudioUnitType_Output) continue; else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) continue; } #endif return noErr; /* found it! */ } return kAUGraphErr_NodeNotFound; }
/** Set the volume of the current sequence. */ static void DoSetVolume() { if (_sequence == NULL) return; AUGraph graph; MusicSequenceGetAUGraph(_sequence, &graph); AudioUnit output_unit = NULL; /* Get output audio unit */ UInt32 node_count = 0; AUGraphGetNodeCount(graph, &node_count); for (UInt32 i = 0; i < node_count; i++) { AUNode node; AUGraphGetIndNode(graph, i, &node); AudioUnit unit; OSType comp_type = 0; #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (MacOSVersionIsAtLeast(10, 5, 0)) { /* The 10.6 SDK has changed the function prototype of * AUGraphNodeInfo. This is a binary compatible change, * but we need to get the type declaration right or * risk compilation errors. The header AudioComponent.h * was introduced in 10.6 so use it to decide which * type definition to use. */ #ifdef __AUDIOCOMPONENT_H__ AudioComponentDescription desc; #else ComponentDescription desc; #endif AUGraphNodeInfo(graph, node, &desc, &unit); comp_type = desc.componentType; } else #endif { #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) ComponentDescription desc; AUGraphGetNodeInfo(graph, node, &desc, NULL, NULL, &unit); comp_type = desc.componentType; #endif } if (comp_type == kAudioUnitType_Output) { output_unit = unit; break; } } if (output_unit == NULL) { DEBUG(driver, 1, "cocoa_m: Failed to get output node to set volume"); return; } Float32 vol = _volume / 127.0f; // 0 - +127 -> 0.0 - 1.0 AudioUnitSetParameter(output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0); }
/* we're looking for the sequence output audiounit. */ static OSStatus GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit) { AUGraph graph; UInt32 nodecount, i; OSStatus err; err = MusicSequenceGetAUGraph(sequence, &graph); if (err != noErr) return err; err = AUGraphGetNodeCount(graph, &nodecount); if (err != noErr) return err; for (i = 0; i < nodecount; i++) { AUNode node; if (AUGraphGetIndNode(graph, i, &node) != noErr) continue; /* better luck next time. */ #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* this is deprecated, but works back to 10.0 */ { struct ComponentDescription desc; UInt32 classdatasize = 0; void *classdata = NULL; err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize, &classdata, aunit); if (err != noErr) continue; else if (desc.componentType != kAudioUnitType_Output) continue; else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) continue; } #else /* not deprecated, but requires 10.5 or later */ { AudioComponentDescription desc; if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr) continue; else if (desc.componentType != kAudioUnitType_Output) continue; else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) continue; } #endif return noErr; /* found it! */ } return kAUGraphErr_NodeNotFound; }
OSStatus GetSynthFromGraph (AUGraph& inGraph, AudioUnit& outSynth) { UInt32 nodeCount; OSStatus result = noErr; FailIf ((result = AUGraphGetNodeCount (inGraph, &nodeCount)), fail, "AUGraphGetNodeCount"); for (UInt32 i = 0; i < nodeCount; ++i) { AUNode node; FailIf ((result = AUGraphGetIndNode(inGraph, i, &node)), fail, "AUGraphGetIndNode"); AudioComponentDescription desc; FailIf ((result = AUGraphNodeInfo(inGraph, node, &desc, 0)), fail, "AUGraphNodeInfo"); if (desc.componentType == kAudioUnitType_MusicDevice) { FailIf ((result = AUGraphNodeInfo(inGraph, node, 0, &outSynth)), fail, "AUGraphNodeInfo"); return noErr; } } fail: // didn't find the synth AU return -1; }
void WriteOutputFile (const char* outputFilePath, OSType dataFormat, Float64 srate, MusicTimeStamp sequenceLength, bool shouldPrint, AUGraph inGraph, UInt32 numFrames, MusicPlayer player) { OSStatus result = 0; UInt32 size; CAStreamBasicDescription outputFormat; outputFormat.mChannelsPerFrame = 2; outputFormat.mSampleRate = srate; outputFormat.mFormatID = dataFormat; AudioFileTypeID destFileType; CAAudioFileFormats::Instance()->InferFileFormatFromFilename (outputFilePath, destFileType); if (dataFormat == kAudioFormatLinearPCM) { outputFormat.mBytesPerPacket = outputFormat.mChannelsPerFrame * 2; outputFormat.mFramesPerPacket = 1; outputFormat.mBytesPerFrame = outputFormat.mBytesPerPacket; outputFormat.mBitsPerChannel = 16; if (destFileType == kAudioFileWAVEType) outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; else outputFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; } else { // use AudioFormat API to fill out the rest. size = sizeof(outputFormat); FailIf ((result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat)), fail, ""); } if (shouldPrint) { printf ("Writing to file: %s with format:\n* ", outputFilePath); outputFormat.Print(); } CFURLRef url; url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*)outputFilePath, strlen(outputFilePath), false); // create output file, delete existing file ExtAudioFileRef outfile; result = ExtAudioFileCreateWithURL(url, destFileType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outfile); if (url) CFRelease (url); FailIf (result, fail, "ExtAudioFileCreateWithURL"); AudioUnit outputUnit; outputUnit = NULL; UInt32 nodeCount; FailIf ((result = AUGraphGetNodeCount (inGraph, &nodeCount)), fail, "AUGraphGetNodeCount"); for (UInt32 i = 0; i < nodeCount; ++i) { AUNode node; FailIf ((result = AUGraphGetIndNode(inGraph, i, &node)), fail, "AUGraphGetIndNode"); AudioComponentDescription desc; FailIf ((result = AUGraphNodeInfo(inGraph, node, &desc, NULL)), fail, "AUGraphNodeInfo"); if (desc.componentType == kAudioUnitType_Output) { FailIf ((result = AUGraphNodeInfo(inGraph, node, 0, &outputUnit)), fail, "AUGraphNodeInfo"); break; } } FailIf ((result = (outputUnit == NULL)), fail, "outputUnit == NULL"); { CAStreamBasicDescription clientFormat = CAStreamBasicDescription(); size = sizeof(clientFormat); FailIf ((result = AudioUnitGetProperty (outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &clientFormat, &size)), fail, "AudioUnitGetProperty: kAudioUnitProperty_StreamFormat"); size = sizeof(clientFormat); FailIf ((result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat)), fail, "ExtAudioFileSetProperty: kExtAudioFileProperty_ClientDataFormat"); { MusicTimeStamp currentTime; AUOutputBL outputBuffer (clientFormat, numFrames); AudioTimeStamp tStamp; memset (&tStamp, 0, sizeof(AudioTimeStamp)); tStamp.mFlags = kAudioTimeStampSampleTimeValid; int i = 0; int numTimesFor10Secs = (int)(10. / (numFrames / srate)); do { outputBuffer.Prepare(); AudioUnitRenderActionFlags actionFlags = 0; FailIf ((result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL())), fail, "AudioUnitRender"); tStamp.mSampleTime += numFrames; FailIf ((result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL())), fail, "ExtAudioFileWrite"); FailIf ((result = MusicPlayerGetTime (player, ¤tTime)), fail, "MusicPlayerGetTime"); if (shouldPrint && (++i % numTimesFor10Secs == 0)) printf ("current time: %6.2f beats\n", currentTime); } while (currentTime < sequenceLength); } } // close ExtAudioFileDispose(outfile); return; fail: printf ("Problem: %ld\n", (long)result); exit(1); }
OSStatus SetUpGraph (AUGraph &inGraph, UInt32 numFrames, Float64 &sampleRate, bool isOffline) { OSStatus result = noErr; AudioUnit outputUnit = 0; AUNode outputNode; // the frame size is the I/O size to the device // the device is going to run at a sample rate it is set at // so, when we set this, we also have to set the max frames for the graph nodes UInt32 nodeCount; FailIf ((result = AUGraphGetNodeCount (inGraph, &nodeCount)), home, "AUGraphGetNodeCount"); for (int i = 0; i < (int)nodeCount; ++i) { AUNode node; FailIf ((result = AUGraphGetIndNode(inGraph, i, &node)), home, "AUGraphGetIndNode"); AudioComponentDescription desc; AudioUnit unit; FailIf ((result = AUGraphNodeInfo(inGraph, node, &desc, &unit)), home, "AUGraphNodeInfo"); if (desc.componentType == kAudioUnitType_Output) { if (outputUnit == 0) { outputUnit = unit; FailIf ((result = AUGraphNodeInfo(inGraph, node, 0, &outputUnit)), home, "AUGraphNodeInfo"); if (!isOffline) { // these two properties are only applicable if its a device we're playing too FailIf ((result = AudioUnitSetProperty (outputUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Output, 0, &numFrames, sizeof(numFrames))), home, "AudioUnitSetProperty: kAudioDevicePropertyBufferFrameSize"); FailIf ((result = AudioUnitAddPropertyListener (outputUnit, kAudioDeviceProcessorOverload, OverlaodListenerProc, 0)), home, "AudioUnitAddPropertyListener: kAudioDeviceProcessorOverload"); // if we're rendering to the device, then we render at its sample rate UInt32 theSize; theSize = sizeof(sampleRate); FailIf ((result = AudioUnitGetProperty (outputUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate, &theSize)), home, "AudioUnitGetProperty: kAudioUnitProperty_SampleRate"); } else { // remove device output node and add generic output FailIf ((result = AUGraphRemoveNode (inGraph, node)), home, "AUGraphRemoveNode"); desc.componentSubType = kAudioUnitSubType_GenericOutput; FailIf ((result = AUGraphAddNode (inGraph, &desc, &node)), home, "AUGraphAddNode"); FailIf ((result = AUGraphNodeInfo(inGraph, node, NULL, &unit)), home, "AUGraphNodeInfo"); outputUnit = unit; outputNode = node; // we render the output offline at the desired sample rate FailIf ((result = AudioUnitSetProperty (outputUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate, sizeof(sampleRate))), home, "AudioUnitSetProperty: kAudioUnitProperty_SampleRate"); } // ok, lets start the loop again now and do it all... i = -1; } } else { // we only have to do this on the output side // as the graph's connection mgmt will propogate this down. if (outputUnit) { // reconnect up to the output unit if we're offline if (isOffline && desc.componentType != kAudioUnitType_MusicDevice) { FailIf ((result = AUGraphConnectNodeInput (inGraph, node, 0, outputNode, 0)), home, "AUGraphConnectNodeInput"); } FailIf ((result = AudioUnitSetProperty (unit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate, sizeof(sampleRate))), home, "AudioUnitSetProperty: kAudioUnitProperty_SampleRate"); } } FailIf ((result = AudioUnitSetProperty (unit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &numFrames, sizeof(numFrames))), home, "AudioUnitSetProperty: kAudioUnitProperty_MaximumFramesPerSlice"); } home: return result; }
void WriteOutputFile (const char* outputFilePath, OSType dataFormat, Float64 srate, MusicTimeStamp sequenceLength, bool shouldPrint, AUGraph inGraph, UInt32 numFrames, MusicPlayer player) { // delete existing output file TestFile (outputFilePath, true); OSStatus result = 0; UInt32 size; CAStreamBasicDescription outputFormat; outputFormat.mChannelsPerFrame = 2; outputFormat.mSampleRate = srate; outputFormat.mFormatID = dataFormat; AudioFileTypeID destFileType; CAAudioFileFormats::Instance()->InferFileFormatFromFilename (outputFilePath, destFileType); if (dataFormat == kAudioFormatLinearPCM) { outputFormat.mBytesPerPacket = outputFormat.mChannelsPerFrame * 2; outputFormat.mFramesPerPacket = 1; outputFormat.mBytesPerFrame = outputFormat.mBytesPerPacket; outputFormat.mBitsPerChannel = 16; if (destFileType == kAudioFileWAVEType) outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; else outputFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; } else { // use AudioFormat API to fill out the rest. size = sizeof(outputFormat); require_noerr (result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat), fail); } if (shouldPrint) { printf ("Writing to file: %s with format:\n* ", outputFilePath); outputFormat.Print(); } FSRef parentDir; CFStringRef destFileName; require_noerr (result = PosixPathToParentFSRefAndName(outputFilePath, parentDir, destFileName), fail); ExtAudioFileRef outfile; result = ExtAudioFileCreateNew (&parentDir, destFileName, destFileType, &outputFormat, NULL, &outfile); CFRelease (destFileName); require_noerr (result, fail); AudioUnit outputUnit; UInt32 nodeCount; require_noerr (result = AUGraphGetNodeCount (inGraph, &nodeCount), fail); for (UInt32 i = 0; i < nodeCount; ++i) { AUNode node; require_noerr (result = AUGraphGetIndNode(inGraph, i, &node), fail); ComponentDescription desc; require_noerr (result = AUGraphNodeInfo(inGraph, node, &desc, NULL), fail); if (desc.componentType == kAudioUnitType_Output) { require_noerr (result = AUGraphNodeInfo(inGraph, node, 0, &outputUnit), fail); break; } } { CAStreamBasicDescription clientFormat; size = sizeof(clientFormat); require_noerr (result = AudioUnitGetProperty (outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &clientFormat, &size), fail); size = sizeof(clientFormat); require_noerr (result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat), fail); { MusicTimeStamp currentTime; AUOutputBL outputBuffer (clientFormat, numFrames); AudioTimeStamp tStamp; memset (&tStamp, 0, sizeof(AudioTimeStamp)); tStamp.mFlags = kAudioTimeStampSampleTimeValid; int i = 0; int numTimesFor10Secs = (int)(10. / (numFrames / srate)); do { outputBuffer.Prepare(); AudioUnitRenderActionFlags actionFlags = 0; require_noerr (result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL()), fail); tStamp.mSampleTime += numFrames; require_noerr (result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()), fail); require_noerr (result = MusicPlayerGetTime (player, ¤tTime), fail); if (shouldPrint && (++i % numTimesFor10Secs == 0)) printf ("current time: %6.2f beats\n", currentTime); } while (currentTime < sequenceLength); } } // close ExtAudioFileDispose(outfile); return; fail: printf ("Problem: %ld\n", result); exit(1); }
JNIEXPORT jint JNICALL Java_com_apple_audio_toolbox_AUGraph_AUGraphGetIndNode (JNIEnv *, jclass, jint inGraph, jint inIndex, jint outNode) { return (jint)AUGraphGetIndNode((AUGraph)inGraph, (UInt32)inIndex, (AUNode *)outNode); }