CAUOutputDevice *CCoreAudioGraph::DestroyUnit(CAUOutputDevice *outputUnit) { if (!outputUnit) return NULL; Stop(); for (AUUnitList::iterator itt = m_auUnitList.begin(); itt != m_auUnitList.end(); ++itt) if (*itt == outputUnit) { m_auUnitList.erase(itt); break; } ReleaseBus(outputUnit->GetBus()); outputUnit->Close(); delete outputUnit; outputUnit = NULL; AUGraphUpdate(m_audioGraph, NULL); printf("Remove unit\n\n"); ShowGraph(); printf("\n"); Start(); return NULL; }
CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format) { if (!m_audioUnit || !m_mixerUnit) return NULL; std::string formatString; AudioStreamBasicDescription inputFormat; AudioStreamBasicDescription outputFormat; OSStatus ret; int busNumber = GetFreeBus(); if (busNumber == INVALID_BUS) return NULL; // create output unit CAUOutputDevice *outputUnit = new CAUOutputDevice(); if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple)) goto error; m_audioUnit->GetFormatDesc(format, &inputFormat); // get the format frm the mixer if (!m_mixerUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus)) goto error; if (!outputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus)) goto error; if (!outputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus)) goto error; ret = AUGraphConnectNodeInput(m_audioGraph, outputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::CreateUnit: Error connecting outputUnit. Error = %s", GetError(ret).c_str()); goto error; } // TODO: setup mixmap, get free bus number for connection outputUnit->SetBus(busNumber); AUGraphUpdate(m_audioGraph, NULL); printf("Add unit\n\n"); ShowGraph(); printf("\n"); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputFormat, formatString)); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputFormat, formatString)); m_auUnitList.push_back(outputUnit); return outputUnit; error: delete outputUnit; return NULL; }
void CCoreAudioUnit::Close() { if (!m_Initialized && !m_audioUnit) return; if (m_renderProc) SetInputSource(NULL); Stop(); if (m_busNumber != INVALID_BUS) { OSStatus ret = AUGraphDisconnectNodeInput(m_audioGraph, m_audioNode, m_busNumber); if (ret) { CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str()); } ret = AUGraphRemoveNode(m_audioGraph, m_audioNode); if (ret) { CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str()); } } AUGraphUpdate(m_audioGraph, NULL); m_Initialized = false; m_audioUnit = NULL; m_audioNode = NULL; m_pSource = NULL; }
CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format) { if (!m_audioUnit || !m_mixerUnit) return NULL; AudioStreamBasicDescription fmt = {0}; AudioStreamBasicDescription inputFormat = {0}; AudioStreamBasicDescription outputFormat = {0}; int busNumber = GetFreeBus(); if (busNumber == INVALID_BUS) return NULL; OSStatus ret; // create output unit CAUOutputDevice *outputUnit = new CAUOutputDevice(); if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple)) goto error; m_audioUnit->GetFormatDesc(format, &inputFormat, &fmt); // get the format frm the mixer if (!m_mixerUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus)) goto error; if (!outputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus)) goto error; if (!outputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus)) goto error; ret = AUGraphConnectNodeInput(m_audioGraph, outputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::CreateUnit: " "Error connecting outputUnit. Error = %s", GetError(ret).c_str()); goto error; } // TODO: setup mixmap, get free bus number for connection outputUnit->SetBus(busNumber); if (m_mixMap || m_mixMap->IsValid()) { UInt32 inputNumber = outputUnit->GetBus(); int channelOffset = GetMixerChannelOffset(inputNumber); CCoreAudioMixMap::SetMixingMatrix(m_mixerUnit, m_mixMap, &inputFormat, &fmt, channelOffset); } AUGraphUpdate(m_audioGraph, NULL); m_auUnitList.push_back(outputUnit); return outputUnit; error: delete outputUnit; return NULL; }
int main(int argc, char *argv[]) { AUGraph audioGraph; NewAUGraph(&audioGraph); AudioComponentDescription cd; AUNode outputNode; AudioUnit outputUnit; cd.componentManufacturer = kAudioUnitManufacturer_Apple; cd.componentFlags = 0; cd.componentFlagsMask = 0; cd.componentType = kAudioUnitType_Output; cd.componentSubType = kAudioUnitSubType_DefaultOutput; AUGraphAddNode(audioGraph, &cd, &outputNode); AUGraphNodeInfo(audioGraph, outputNode, &cd, &outputUnit); AUNode mixerNode; AudioUnit mixerUnit; cd.componentManufacturer = kAudioUnitManufacturer_Apple; cd.componentFlags = 0; cd.componentFlagsMask = 0; cd.componentType = kAudioUnitType_Mixer; cd.componentSubType = kAudioUnitSubType_StereoMixer; AUGraphAddNode(audioGraph, &cd, &mixerNode); AUGraphNodeInfo(audioGraph, mixerNode, &cd, &mixerUnit); AUGraphConnectNodeInput(audioGraph, mixerNode, 0, outputNode, 0); AUGraphOpen(audioGraph); AUGraphInitialize(audioGraph); AUGraphStart(audioGraph); AUNode synthNode; AudioUnit synthUnit; cd.componentManufacturer = kAudioUnitManufacturer_Apple; cd.componentFlags = 0; cd.componentFlagsMask = 0; cd.componentType = kAudioUnitType_MusicDevice; cd.componentSubType = kAudioUnitSubType_DLSSynth; AUGraphAddNode(audioGraph, &cd, &synthNode); AUGraphNodeInfo(audioGraph, synthNode, &cd, &synthUnit); AUGraphConnectNodeInput(audioGraph, synthNode, 0, mixerNode, 0); AUGraphUpdate(audioGraph, NULL); CAShow(audioGraph); MusicDeviceMIDIEvent(synthUnit, 0x90, 60, 127, 0); sleep(1); MusicDeviceMIDIEvent(synthUnit, 0x90, 62, 127, 0); sleep(1); MusicDeviceMIDIEvent(synthUnit, 0x90, 64, 127, 0); sleep(1); sleep(5); return(0); }
bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing) { OSStatus ret; AudioStreamBasicDescription inputFormat; AudioStreamBasicDescription outputFormat; m_allowMixing = allowMixing; ret = NewAUGraph(&m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error create audio grpah. Error = %s", GetError(ret).c_str()); return false; } ret = AUGraphOpen(m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error open audio grpah. Error = %s", GetError(ret).c_str()); return false; } // get output unit if (m_audioUnit) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error audio unit already open. double call ?"); return false; } m_audioUnit = new CAUOutputDevice(); if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple)) return false; if (!m_audioUnit->EnableInputOuput()) return false; m_audioUnit->GetFormatDesc(format, &inputFormat); //if(!allowMixing) //{ if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus)) return false; if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus)) return false; //} if (allowMixing) { // get mixer unit if (m_mixerUnit) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?"); return false; } m_mixerUnit = new CAUMultiChannelMixer(); if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MultiChannelMixer, kAudioUnitManufacturer_Apple)) return false; // set number of input buses if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT)) return false; //if(!m_mixerUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus)) // return false; m_mixerUnit->SetBus(0); if (!m_audioUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus)) return false; /* if(!m_mixerUnit->SetInputBusFormat(MAX_CONNECTION_LIMIT, &outputFormat)) return false; */ ret = AUGraphConnectNodeInput(m_audioGraph, m_mixerUnit->GetNode(), 0, m_audioUnit->GetNode(), 0); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_m_mixerNode. Error = %s", GetError(ret).c_str()); return false; } // get output unit if (m_inputUnit) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?"); return false; } m_inputUnit = new CAUOutputDevice(); if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple)) return false; if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus)) return false; /* if(!m_inputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus)) return false; */ // configure output unit int busNumber = GetFreeBus(); ret = AUGraphConnectNodeInput(m_audioGraph, m_inputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_converterNode. Error = %s", GetError(ret).c_str()); return false; } m_inputUnit->SetBus(busNumber); ret = AUGraphUpdate(m_audioGraph, NULL); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str()); return false; } ret = AUGraphInitialize(m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str()); return false; } // Regenerate audio format and copy format for the Output AU } ret = AUGraphUpdate(m_audioGraph, NULL); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str()); return false; } std::string formatString; AudioStreamBasicDescription inputDesc_end, outputDesc_end; m_audioUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus); m_audioUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kInputBus); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString)); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString)); if (m_mixerUnit) { m_mixerUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus); m_mixerUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString)); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString)); } if (m_inputUnit) { m_inputUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus); m_inputUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString)); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString)); } ret = AUGraphInitialize(m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str()); return false; } UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize(); m_audioUnit->SetMaxFramesPerSlice(bufferFrames); if (m_inputUnit) m_inputUnit->SetMaxFramesPerSlice(bufferFrames); SetInputSource(pSource); ShowGraph(); return Start(); }
bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, AudioDeviceID deviceId, bool allowMixing, AudioChannelLayoutTag layoutTag) { AudioStreamBasicDescription fmt = {0}; AudioStreamBasicDescription inputFormat = {0}; AudioStreamBasicDescription outputFormat = {0}; m_deviceId = deviceId; m_allowMixing = allowMixing; OSStatus ret = NewAUGraph(&m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error create audio grpah. Error = %s", GetError(ret).c_str()); return false; } ret = AUGraphOpen(m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error open audio grpah. Error = %s", GetError(ret).c_str()); return false; } // get output unit if (m_audioUnit) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error audio unit already open. double call ?"); return false; } m_audioUnit = new CAUOutputDevice(); if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple)) return false; m_audioUnit->SetBus(GetFreeBus()); m_audioUnit->GetFormatDesc(format, &inputFormat, &fmt); if (!m_audioUnit->EnableInputOuput()) return false; if (!m_audioUnit->SetCurrentDevice(deviceId)) return false; if (allowMixing) { delete m_mixMap; m_mixMap = CCoreAudioMixMap::CreateMixMap(m_audioUnit, format, layoutTag); if (m_mixMap || m_mixMap->IsValid()) { // maximum input channel ber input bus //fmt.mChannelsPerFrame = MAXIMUM_MIXER_CHANNELS; // get output unit if (m_inputUnit) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?"); return false; } m_inputUnit = new CAUOutputDevice(); if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple)) return false; if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus)) return false; if (!m_inputUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus)) return false; // get mixer unit if (m_mixerUnit) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?"); return false; } m_mixerUnit = new CAUMatrixMixer(); if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple)) return false; // set number of input buses if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT)) return false; // set number of output buses if (!m_mixerUnit->SetOutputBusCount(1)) return false; if (!m_mixerUnit->SetInputBusFormat(MAX_CONNECTION_LIMIT, &fmt)) return false; if (!m_mixerUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus)) return false; ret = AUGraphConnectNodeInput(m_audioGraph, m_mixerUnit->GetNode(), 0, m_audioUnit->GetNode(), 0); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error connecting m_m_mixerNode. Error = %s", GetError(ret).c_str()); return false; } m_mixerUnit->SetBus(0); // configure output unit int busNumber = GetFreeBus(); ret = AUGraphConnectNodeInput(m_audioGraph, m_inputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error connecting m_converterNode. Error = %s", GetError(ret).c_str()); return false; } m_inputUnit->SetBus(busNumber); ret = AUGraphUpdate(m_audioGraph, NULL); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error update graph. Error = %s", GetError(ret).c_str()); return false; } ret = AUGraphInitialize(m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error initialize graph. Error = %s", GetError(ret).c_str()); return false; } // Update format structure to reflect the desired format from the mixer // The output format of the mixer is identical to the input format, except for the channel count fmt.mChannelsPerFrame = m_mixMap->GetOutputChannels(); UInt32 inputNumber = m_inputUnit->GetBus(); int channelOffset = GetMixerChannelOffset(inputNumber); if (!CCoreAudioMixMap::SetMixingMatrix(m_mixerUnit, m_mixMap, &inputFormat, &fmt, channelOffset)) return false; // Regenerate audio format and copy format for the Output AU outputFormat = fmt; } else { outputFormat = inputFormat; } } else { outputFormat = inputFormat; } if (!m_audioUnit->SetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus)) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error setting input format on audio device. Channel count %d, set it to %d", (int)outputFormat.mChannelsPerFrame, format.m_channelLayout.Count()); outputFormat.mChannelsPerFrame = format.m_channelLayout.Count(); if (!m_audioUnit->SetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus)) return false; } std::string formatString; // asume we are in dd-wave mode if (!m_inputUnit) { if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus)) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error setting Device Output Stream Format %s", StreamDescriptionToString(inputFormat, formatString)); } } ret = AUGraphUpdate(m_audioGraph, NULL); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error update graph. Error = %s", GetError(ret).c_str()); return false; } AudioStreamBasicDescription inputDesc_end, outputDesc_end; m_audioUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus); m_audioUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kInputBus); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString)); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString)); if (m_mixerUnit) { m_mixerUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus); m_mixerUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString)); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString)); } if (m_inputUnit) { m_inputUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus); m_inputUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString)); CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString)); } ret = AUGraphInitialize(m_audioGraph); if (ret) { CLog::Log(LOGERROR, "CCoreAudioGraph::Open: " "Error initialize graph. Error = %s", GetError(ret).c_str()); return false; } UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize(); if (!m_audioUnit->SetMaxFramesPerSlice(bufferFrames)) return false; SetInputSource(pSource); return Start(); }
bool FCoreAudioSoundSource::DetachFromAUGraph() { AURenderCallbackStruct Input; Input.inputProc = NULL; Input.inputProcRefCon = NULL; SAFE_CA_CALL( AudioUnitSetProperty( SourceUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &Input, sizeof( Input ) ) ); if( StreamSplitterNode ) { SAFE_CA_CALL( AUGraphDisconnectNodeInput( AudioDevice->GetAudioUnitGraph(), StreamSplitterNode, 0 ) ); } if( ReverbNode ) { SAFE_CA_CALL( AUGraphDisconnectNodeInput( AudioDevice->GetAudioUnitGraph(), ReverbNode, 0 ) ); } if( RadioNode ) { SAFE_CA_CALL( AUGraphDisconnectNodeInput( AudioDevice->GetAudioUnitGraph(), RadioNode, 0 ) ); } if( EQNode ) { SAFE_CA_CALL( AUGraphDisconnectNodeInput( AudioDevice->GetAudioUnitGraph(), EQNode, 0 ) ); } if( StreamMergerNode ) { SAFE_CA_CALL( AUGraphDisconnectNodeInput( AudioDevice->GetAudioUnitGraph(), StreamMergerNode, 0 ) ); } if( AudioChannel ) { if( Buffer->NumChannels < 3 ) { SAFE_CA_CALL( AUGraphDisconnectNodeInput( AudioDevice->GetAudioUnitGraph(), AudioDevice->GetMixer3DNode(), MixerInputNumber ) ); AudioDevice->SetFreeMixer3DInput( MixerInputNumber ); } else { SAFE_CA_CALL( AUGraphDisconnectNodeInput( AudioDevice->GetAudioUnitGraph(), AudioDevice->GetMatrixMixerNode(), MixerInputNumber ) ); AudioDevice->SetFreeMatrixMixerInput( MixerInputNumber ); } } if( StreamMergerNode ) { SAFE_CA_CALL( AUGraphRemoveNode( AudioDevice->GetAudioUnitGraph(), StreamMergerNode ) ); } if( EQNode ) { SAFE_CA_CALL( AUGraphRemoveNode( AudioDevice->GetAudioUnitGraph(), EQNode ) ); } if( RadioNode ) { SAFE_CA_CALL( AUGraphRemoveNode( AudioDevice->GetAudioUnitGraph(), RadioNode ) ); } if( ReverbNode ) { SAFE_CA_CALL( AUGraphRemoveNode( AudioDevice->GetAudioUnitGraph(), ReverbNode ) ); } if( StreamSplitterNode ) { SAFE_CA_CALL( AUGraphRemoveNode( AudioDevice->GetAudioUnitGraph(), StreamSplitterNode ) ); } if( AudioChannel ) { SAFE_CA_CALL( AUGraphRemoveNode( AudioDevice->GetAudioUnitGraph(), SourceNode ) ); } SAFE_CA_CALL( AUGraphUpdate( AudioDevice->GetAudioUnitGraph(), NULL ) ); AudioConverterDispose( CoreAudioConverter ); CoreAudioConverter = NULL; StreamMergerNode = 0; StreamMergerUnit = NULL; EQNode = 0; EQUnit = NULL; RadioNode = 0; RadioUnit = NULL; ReverbNode = 0; ReverbUnit = NULL; StreamSplitterNode = 0; StreamSplitterUnit = NULL; SourceNode = 0; SourceUnit = NULL; MixerInputNumber = -1; GAudioChannels[AudioChannel] = NULL; AudioChannel = 0; return true; }
bool FCoreAudioSoundSource::AttachToAUGraph() { AudioChannel = FindFreeAudioChannel(); OSStatus ErrorStatus = noErr; AUNode DestNode = -1; int32 DestInputNumber = 0; AudioStreamBasicDescription* StreamFormat = NULL; if( Buffer->NumChannels < 3 ) { ErrorStatus = AudioConverterNew( &Buffer->PCMFormat, &AudioDevice->Mixer3DFormat, &CoreAudioConverter ); DestNode = AudioDevice->GetMixer3DNode(); MixerInputNumber = DestInputNumber = AudioDevice->GetFreeMixer3DInput(); uint32 SpatialSetting = ( Buffer->NumChannels == 1 ) ? kSpatializationAlgorithm_SoundField : kSpatializationAlgorithm_StereoPassThrough; AudioUnitSetProperty( AudioDevice->GetMixer3DUnit(), kAudioUnitProperty_SpatializationAlgorithm, kAudioUnitScope_Input, MixerInputNumber, &SpatialSetting, sizeof( SpatialSetting ) ); AudioUnitSetParameter( AudioDevice->GetMixer3DUnit(), k3DMixerParam_Distance, kAudioUnitScope_Input, MixerInputNumber, 1.0f, 0 ); StreamFormat = &AudioDevice->Mixer3DFormat; } else { MixerInputNumber = DestInputNumber = AudioDevice->GetFreeMatrixMixerInput(); DestNode = AudioDevice->GetMatrixMixerNode(); StreamFormat = &AudioDevice->MatrixMixerInputFormat; ErrorStatus = AudioConverterNew( &Buffer->PCMFormat, &AudioDevice->MatrixMixerInputFormat, &CoreAudioConverter ); bool bIs6ChannelOGG = Buffer->NumChannels == 6 && (Buffer->DecompressionState || WaveInstance->WaveData->bDecompressedFromOgg); AudioDevice->SetupMatrixMixerInput( DestInputNumber, bIs6ChannelOGG ); } if( ErrorStatus != noErr ) { UE_LOG(LogCoreAudio, Warning, TEXT("CoreAudioConverter creation failed, error code %d"), ErrorStatus); } int FiltersNeeded = 0; #if EQ_ENABLED bool bNeedEQFilter = IsEQFilterApplied(); if( bNeedEQFilter ) { ++FiltersNeeded; } #else bool bNeedEQFilter = false; #endif #if RADIO_ENABLED bool bNeedRadioFilter = Effects->bRadioAvailable && WaveInstance->bApplyRadioFilter; if( bNeedRadioFilter ) { ++FiltersNeeded; } #else bool bNeedRadioFilter = false; #endif #if REVERB_ENABLED bool bNeedReverbFilter = bReverbApplied; if( bNeedReverbFilter ) { ++FiltersNeeded; } #else bool bNeedReverbFilter = false; #endif if( FiltersNeeded > 0 ) { uint32 BusCount = FiltersNeeded + ( bNeedEQFilter ? 0 : 1 ); // one for each filter, plus one for dry voice if there's no EQ filter // Prepare Voice Merger SAFE_CA_CALL( CreateAudioUnit( kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple, NULL, NULL, &StreamMergerNode, &StreamMergerUnit ) ); // Set Bus Counts uint32 NumBuses = BusCount; SAFE_CA_CALL( AudioUnitSetProperty( StreamMergerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &NumBuses, sizeof(uint32) ) ); NumBuses = 1; SAFE_CA_CALL( AudioUnitSetProperty( StreamMergerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &NumBuses, sizeof(uint32) ) ); // Set Input Formats for( int32 InputIndex = 0; InputIndex < BusCount; ++InputIndex ) { SAFE_CA_CALL( AudioUnitSetProperty( StreamMergerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, InputIndex, StreamFormat, sizeof( AudioStreamBasicDescription ) ) ); } // Set Output Format SAFE_CA_CALL( AudioUnitSetProperty( StreamMergerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, StreamFormat, sizeof( AudioStreamBasicDescription ) ) ); SAFE_CA_CALL( AudioUnitInitialize( StreamMergerUnit ) ); // Set Master volume SAFE_CA_CALL( AudioUnitSetParameter( StreamMergerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.0, 0 ) ); // Enable Output SAFE_CA_CALL( AudioUnitSetParameter( StreamMergerUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Output, 0, 1.0, 0 ) ); // Set Output volumes for( int32 ChannelIndex = 0; ChannelIndex < StreamFormat->mChannelsPerFrame; ++ChannelIndex ) { SAFE_CA_CALL( AudioUnitSetParameter( StreamMergerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, ChannelIndex, 1.0, 0 ) ); } for( int32 InputIndex = 0; InputIndex < BusCount; ++InputIndex ) { // Enable Input SAFE_CA_CALL( AudioUnitSetParameter( StreamMergerUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Input, InputIndex, 1.0, 0 ) ); } for( int32 ChannelIndex = 0; ChannelIndex < StreamFormat->mChannelsPerFrame; ++ChannelIndex ) { for( int32 InputBusIndex = 0; InputBusIndex < BusCount; ++InputBusIndex ) { int32 InputChannelIndex = InputBusIndex*StreamFormat->mChannelsPerFrame + ChannelIndex; // Set Input Channel Volume SAFE_CA_CALL( AudioUnitSetParameter( StreamMergerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Input, InputChannelIndex, 1.0, 0 ) ); // Set Crossfade Volume - each input channel goes to specific output channel. The rest of connections is left at zero. SAFE_CA_CALL( AudioUnitSetParameter( StreamMergerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( InputChannelIndex << 16 ) | ChannelIndex, 1.0, 0 ) ); } } SAFE_CA_CALL( AUGraphConnectNodeInput( AudioDevice->GetAudioUnitGraph(), StreamMergerNode, 0, DestNode, DestInputNumber ) ); DestNode = StreamMergerNode; DestInputNumber = 0; // Prepare and initialize stream splitter SAFE_CA_CALL( CreateAudioUnit( kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple, NULL, NULL, &StreamSplitterNode, &StreamSplitterUnit ) ); // Set bus counts NumBuses = 1; SAFE_CA_CALL( AudioUnitSetProperty( StreamSplitterUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &NumBuses, sizeof(uint32) ) ); NumBuses = BusCount; SAFE_CA_CALL( AudioUnitSetProperty( StreamSplitterUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &NumBuses, sizeof(uint32) ) ); // Set Input format SAFE_CA_CALL( AudioUnitSetProperty( StreamSplitterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, StreamFormat, sizeof( AudioStreamBasicDescription ) ) ); // Set Output formats for( int32 OutputIndex = 0; OutputIndex < BusCount; ++OutputIndex ) { SAFE_CA_CALL( AudioUnitSetProperty( StreamSplitterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, OutputIndex, StreamFormat, sizeof( AudioStreamBasicDescription ) ) ); } SAFE_CA_CALL( AudioUnitInitialize( StreamSplitterUnit ) ); // Set Master volume SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.0, 0 ) ); // Enable Input SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Input, 0, 1.0, 0 ) ); // Set Input Volumes for( int32 ChannelIndex = 0; ChannelIndex < StreamFormat->mChannelsPerFrame; ++ChannelIndex ) { SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Input, ChannelIndex, 1.0, 0 ) ); } for( int32 OutputIndex = 0; OutputIndex < BusCount; ++OutputIndex ) { // Enable Output SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Output, OutputIndex, 1.0, 0 ) ); } for( int32 ChannelIndex = 0; ChannelIndex < StreamFormat->mChannelsPerFrame; ++ChannelIndex ) { for( int32 OutputBusIndex = 0; OutputBusIndex < BusCount; ++OutputBusIndex ) { int32 OutputChannelIndex = OutputBusIndex*StreamFormat->mChannelsPerFrame + ChannelIndex; // Set Output Channel Volume SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, OutputChannelIndex, 1.0, 0 ) ); // Set Crossfade Volume - each output channel goes from specific input channel. The rest of connections is left at zero. SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( ChannelIndex << 16 ) | OutputChannelIndex, 1.0, 0 ) ); } } // Prepare and connect appropriate filters #if EQ_ENABLED if( bNeedEQFilter ) { SAFE_CA_CALL( CreateAndConnectAudioUnit( kAudioUnitType_Effect, kAudioUnitSubType_AUFilter, kAudioUnitManufacturer_Apple, DestNode, DestInputNumber, StreamFormat, StreamFormat, &EQNode, &EQUnit ) ); SAFE_CA_CALL( AUGraphConnectNodeInput( AudioDevice->GetAudioUnitGraph(), StreamSplitterNode, DestInputNumber, EQNode, 0 ) ); } else #endif { // Add direct connection between stream splitter and stream merger, for dry voice SAFE_CA_CALL( AUGraphConnectNodeInput( AudioDevice->GetAudioUnitGraph(), StreamSplitterNode, 0, StreamMergerNode, 0 ) ); // Silencing dry voice (for testing) // for( int32 ChannelIndex = 0; ChannelIndex < StreamFormat->mChannelsPerFrame; ++ChannelIndex ) // { // SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, ChannelIndex, 0.0, 0 ) ); // } // SAFE_CA_CALL( AudioUnitSetParameter( StreamSplitterUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Output, 0, 0.0, 0 ) ); } ++DestInputNumber; #if RADIO_ENABLED if( bNeedRadioFilter ) { SAFE_CA_CALL( CreateAndConnectAudioUnit( kAudioUnitType_Effect, 'Rdio', 'Epic', DestNode, DestInputNumber, StreamFormat, StreamFormat, &RadioNode, &RadioUnit ) ); SAFE_CA_CALL( AUGraphConnectNodeInput( AudioDevice->GetAudioUnitGraph(), StreamSplitterNode, DestInputNumber, RadioNode, 0 ) ); ++DestInputNumber; } #endif #if REVERB_ENABLED if( bNeedReverbFilter ) { SAFE_CA_CALL( CreateAndConnectAudioUnit( kAudioUnitType_Effect, kAudioUnitSubType_MatrixReverb, kAudioUnitManufacturer_Apple, DestNode, DestInputNumber, StreamFormat, StreamFormat, &ReverbNode, &ReverbUnit ) ); SAFE_CA_CALL( AUGraphConnectNodeInput( AudioDevice->GetAudioUnitGraph(), StreamSplitterNode, DestInputNumber, ReverbNode, 0 ) ); ++DestInputNumber; } #endif DestNode = StreamSplitterNode; DestInputNumber = 0; } SAFE_CA_CALL( CreateAndConnectAudioUnit( kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple, DestNode, DestInputNumber, StreamFormat, StreamFormat, &SourceNode, &SourceUnit ) ); if( ErrorStatus == noErr ) { AURenderCallbackStruct Input; Input.inputProc = &CoreAudioRenderCallback; Input.inputProcRefCon = this; SAFE_CA_CALL( AudioUnitSetProperty( SourceUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &Input, sizeof( Input ) ) ); SAFE_CA_CALL( AUGraphUpdate( AudioDevice->GetAudioUnitGraph(), NULL ) ); GAudioChannels[AudioChannel] = this; } return ErrorStatus == noErr; }
JNIEXPORT jint JNICALL Java_com_apple_audio_toolbox_AUGraph_AUGraphUpdate (JNIEnv *, jclass, jint inGraph, jint outIsUpdated) { return (jint)AUGraphUpdate((AUGraph)inGraph, (Boolean *)outIsUpdated); }