2 * Copyright (C) 2011-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #if defined(TARGET_DARWIN_IOS)
24 #include "CoreAudioAEHALIOS.h"
26 #include "xbmc/cores/AudioEngine/Utils/AEUtil.h"
27 #include "AEFactory.h"
28 #include "CoreAudioAE.h"
29 #include "utils/log.h"
33 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
35 // use the maximum frames per slice allows audio play when the screen is locked
36 #define BUFFERED_FRAMES 4096
38 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
39 // CIOSCoreAudioHardware
40 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
42 AudioComponentInstance CIOSCoreAudioHardware::FindAudioDevice(std::string searchName)
44 if (!searchName.length())
47 AudioComponentInstance defaultDevice = GetDefaultOutputDevice();
52 AudioComponentInstance CIOSCoreAudioHardware::GetDefaultOutputDevice()
54 AudioComponentInstance ret = (AudioComponentInstance)1;
59 UInt32 CIOSCoreAudioHardware::GetOutputDevices(IOSCoreAudioDeviceList* pList)
67 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
69 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
70 CCoreAudioUnit::CCoreAudioUnit() :
75 m_Initialized (false ),
77 m_busNumber (INVALID_BUS )
81 CCoreAudioUnit::~CCoreAudioUnit()
86 bool CCoreAudioUnit::Open(AUGraph audioGraph, AudioComponentDescription desc)
93 m_Initialized = false;
95 ret = AUGraphAddNode(audioGraph, &desc, &m_audioNode);
98 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error add m_outputNode. Error = %s", GetError(ret).c_str());
102 ret = AUGraphNodeInfo(audioGraph, m_audioNode, NULL, &m_audioUnit);
105 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error getting m_outputNode. Error = %s", GetError(ret).c_str());
109 m_audioGraph = audioGraph;
110 m_Initialized = true;
117 bool CCoreAudioUnit::Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer)
119 AudioComponentDescription desc;
120 desc.componentType = type;
121 desc.componentSubType = subType;
122 desc.componentManufacturer = manufacturer;
123 desc.componentFlags = 0;
124 desc.componentFlagsMask = 0;
125 return Open(audioGraph, desc);
128 void CCoreAudioUnit::Close()
130 if (!m_Initialized && !m_audioUnit)
134 SetInputSource(NULL);
138 if (m_busNumber != INVALID_BUS)
140 OSStatus ret = AUGraphDisconnectNodeInput(m_audioGraph, m_audioNode, m_busNumber);
143 CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
146 ret = AUGraphRemoveNode(m_audioGraph, m_audioNode);
149 CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
153 AUGraphUpdate(m_audioGraph, NULL);
155 m_Initialized = false;
161 bool CCoreAudioUnit::GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
163 if (!m_audioUnit || !pDesc)
166 UInt32 size = sizeof(AudioStreamBasicDescription);
167 OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, &size);
170 CLog::Log(LOGERROR, "CCoreAudioUnit::GetFormat: Unable to get AudioUnit format. Bus : %d Scope : %d : Error = %s", (int)scope, (int)bus, GetError(ret).c_str());
176 bool CCoreAudioUnit::SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
178 if (!m_audioUnit || !pDesc)
181 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, sizeof(AudioStreamBasicDescription));
184 CLog::Log(LOGERROR, "CCoreAudioUnit::SetFormat: Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s", (int)scope, (int)bus, GetError(ret).c_str());
190 bool CCoreAudioUnit::SetMaxFramesPerSlice(UInt32 maxFrames)
195 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(UInt32));
198 CLog::Log(LOGERROR, "CCoreAudioUnit::SetMaxFramesPerSlice: Unable to set AudioUnit max frames per slice. Error = %s", GetError(ret).c_str());
204 bool CCoreAudioUnit::SetInputSource(ICoreAudioSource* pSource)
208 return SetRenderProc();
210 return RemoveRenderProc();
213 bool CCoreAudioUnit::SetRenderProc()
215 if (!m_audioUnit || m_renderProc)
218 AURenderCallbackStruct callbackInfo;
219 callbackInfo.inputProc = RenderCallback; // Function to be called each time the AudioUnit needs data
220 callbackInfo.inputProcRefCon = this; // Pointer to be returned in the callback proc
221 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
222 kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
225 CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: Unable to set AudioUnit render callback. Error = %s", GetError(ret).c_str());
229 m_renderProc = RenderCallback;
234 bool CCoreAudioUnit::RemoveRenderProc()
236 if (!m_audioUnit || !m_renderProc)
240 AURenderCallbackStruct callbackInfo;
241 callbackInfo.inputProc = nil;
242 callbackInfo.inputProcRefCon = nil;
243 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
244 kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
247 CLog::Log(LOGERROR, "CCoreAudioUnit::RemoveRenderProc: Unable to remove AudioUnit render callback. Error = %s", GetError(ret).c_str());
258 OSStatus CCoreAudioUnit::RenderCallback(void *inRefCon,
259 AudioUnitRenderActionFlags *ioActionFlags,
260 const AudioTimeStamp *inTimeStamp,
262 UInt32 inNumberFrames,
263 AudioBufferList *ioData)
265 OSStatus ret = noErr;
266 CCoreAudioUnit *audioUnit = (CCoreAudioUnit*)inRefCon;
268 if (audioUnit->m_pSource)
270 ret = audioUnit->m_pSource->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
274 ioData->mBuffers[0].mDataByteSize = 0;
276 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
283 void CCoreAudioUnit::GetFormatDesc(AEAudioFormat format,
284 AudioStreamBasicDescription *streamDesc)
286 unsigned int bps = CAEUtil::DataFormatToBits(format.m_dataFormat);
288 // Set the input stream format for the AudioUnit
289 // We use the default DefaultOuput AudioUnit, so we only can set the input stream format.
290 // The autput format is automaticaly set to the input format.
291 streamDesc->mFormatID = kAudioFormatLinearPCM; // Data encoding format
292 streamDesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
293 switch (format.m_dataFormat)
296 streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
297 streamDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
305 streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
306 streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
309 streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
312 streamDesc->mFormatFlags |= kAudioFormatFlagIsBigEndian;
313 streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
316 streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
317 streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
320 streamDesc->mChannelsPerFrame = format.m_channelLayout.Count(); // Number of interleaved audiochannels
321 streamDesc->mSampleRate = (Float64)format.m_sampleRate; // the sample rate of the audio stream
322 streamDesc->mBitsPerChannel = bps; // Number of bits per sample, per channel
323 streamDesc->mBytesPerFrame = (bps>>3) * format.m_channelLayout.Count(); // Size of a frame == 1 sample per channel
324 streamDesc->mFramesPerPacket = 1; // The smallest amount of indivisible data. Always 1 for uncompressed audio
325 streamDesc->mBytesPerPacket = streamDesc->mBytesPerFrame * streamDesc->mFramesPerPacket;
326 streamDesc->mReserved = 0;
329 float CCoreAudioUnit::GetLatency()
334 //kAudioSessionProperty_CurrentHardwareIOBufferDuration
335 //kAudioSessionProperty_CurrentHardwareOutputLatency
337 Float32 preferredBufferSize = 0.0f;
338 UInt32 size = sizeof(preferredBufferSize);
339 AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency, &size, &preferredBufferSize);
340 return preferredBufferSize;
343 bool CCoreAudioUnit::SetSampleRate(Float64 sampleRate, AudioUnitScope scope, AudioUnitElement bus)
348 UInt32 size = sizeof(Float64);
350 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SampleRate, scope, bus, &sampleRate, size);
353 CLog::Log(LOGERROR, "CCoreAudioUnit::SetSampleRate: Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s", (int)scope, (int)bus, GetError(ret).c_str());
359 bool CCoreAudioUnit::Stop()
364 AudioOutputUnitStop(m_audioUnit);
369 bool CCoreAudioUnit::Start()
374 AudioOutputUnitStart(m_audioUnit);
379 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
381 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
382 CAUOutputDevice::CAUOutputDevice()
386 CAUOutputDevice::~CAUOutputDevice()
391 Float32 CAUOutputDevice::GetCurrentVolume()
396 Float32 volPct = 0.0f;
397 OSStatus ret = AudioUnitGetParameter(m_audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &volPct);
400 CLog::Log(LOGERROR, "CCoreAudioUnit::GetCurrentVolume: Unable to get AudioUnit volume. Error = %s", GetError(ret).c_str());
406 bool CAUOutputDevice::SetCurrentVolume(Float32 vol)
411 OSStatus ret = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume,
412 kAudioUnitScope_Global, 0, vol, 0);
415 CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentVolume: Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
422 UInt32 CAUOutputDevice::GetBufferFrameSize()
427 return BUFFERED_FRAMES;
430 bool CAUOutputDevice::EnableInputOuput()
438 UInt32 size=sizeof(UInt32);
440 ret = AudioUnitGetProperty(m_audioUnit,kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
445 ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enable, sizeof(enable));
448 CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to enable input on bus 1. Error = %s", GetError(ret).c_str());
453 ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &enable, sizeof(enable));
456 CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to disable output on bus 0. Error = %s", GetError(ret).c_str());
464 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
465 // CAUMultiChannelMixer
466 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
467 CAUMultiChannelMixer::CAUMultiChannelMixer()
471 CAUMultiChannelMixer::~CAUMultiChannelMixer()
476 UInt32 CAUMultiChannelMixer::GetInputBusCount()
482 UInt32 size = sizeof(busCount);
483 OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, &size);
486 CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetInputBusCount: Unable to get input bus count. Error = %s", GetError(ret).c_str());
492 bool CAUMultiChannelMixer::SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat)
497 for (UInt32 i = 0; i < busCount; i++)
499 if (!SetFormat(pFormat, kAudioUnitScope_Input, i))
506 bool CAUMultiChannelMixer::SetInputBusCount(UInt32 busCount)
511 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(UInt32));
514 CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetInputBusCount: Unable to set input bus count. Error = %s", GetError(ret).c_str());
520 UInt32 CAUMultiChannelMixer::GetOutputBusCount()
526 UInt32 size = sizeof(busCount);
527 OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, &size);
530 CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetOutputBusCount: Unable to get output bus count. Error = %s", GetError(ret).c_str());
536 bool CAUMultiChannelMixer::SetOutputBusCount(UInt32 busCount)
541 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, sizeof(UInt32));
544 CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetOutputBusCount: Unable to set output bus count. Error = %s", GetError(ret).c_str());
550 Float32 CAUMultiChannelMixer::GetCurrentVolume()
556 Float32 volPct = 0.0f;
557 OSStatus ret = AudioUnitGetParameter(m_audioUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, kInputBus, &volPct);
560 CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetCurrentVolume: Unable to get Mixer volume. Error = %s", GetError(ret).c_str());
567 bool CAUMultiChannelMixer::SetCurrentVolume(Float32 vol)
573 OSStatus ret = AudioUnitSetParameter(m_audioUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, kOutputBus, vol, 0);
576 CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetCurrentVolume: Unable to set Mixer volume. Error = %s", GetError(ret).c_str());
583 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
585 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
586 CCoreAudioGraph::CCoreAudioGraph() :
587 m_audioGraph (NULL ),
591 m_initialized (false),
592 m_allowMixing (false)
594 for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
596 m_reservedBusNumber[i] = false;
600 CCoreAudioGraph::~CCoreAudioGraph()
605 bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing, float initVolume)
609 AudioStreamBasicDescription inputFormat;
610 AudioStreamBasicDescription outputFormat;
612 m_allowMixing = allowMixing;
614 ret = NewAUGraph(&m_audioGraph);
617 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error create audio grpah. Error = %s", GetError(ret).c_str());
620 ret = AUGraphOpen(m_audioGraph);
623 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error open audio grpah. Error = %s", GetError(ret).c_str());
630 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error audio unit already open. double call ?");
634 m_audioUnit = new CAUOutputDevice();
635 if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple))
638 UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
640 if (!m_audioUnit->EnableInputOuput())
643 m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
645 m_audioUnit->GetFormatDesc(format, &inputFormat);
649 if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
652 if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus))
661 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
665 m_mixerUnit = new CAUMultiChannelMixer();
667 if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MultiChannelMixer, kAudioUnitManufacturer_Apple))
670 m_mixerUnit->SetMaxFramesPerSlice(bufferFrames);
672 // set number of input buses
673 if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT))
676 //if(!m_mixerUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus))
679 m_mixerUnit->SetBus(0);
681 if (!m_audioUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
685 if(!m_mixerUnit->SetInputBusFormat(MAX_CONNECTION_LIMIT, &outputFormat))
689 ret = AUGraphConnectNodeInput(m_audioGraph, m_mixerUnit->GetNode(), 0, m_audioUnit->GetNode(), 0);
692 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_m_mixerNode. Error = %s", GetError(ret).c_str());
699 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
703 m_inputUnit = new CAUOutputDevice();
705 if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
708 m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
710 if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
714 if(!m_inputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
718 // configure output unit
719 int busNumber = GetFreeBus();
721 ret = AUGraphConnectNodeInput(m_audioGraph, m_inputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
724 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_converterNode. Error = %s", GetError(ret).c_str());
728 m_inputUnit->SetBus(busNumber);
730 ret = AUGraphUpdate(m_audioGraph, NULL);
733 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
736 ret = AUGraphInitialize(m_audioGraph);
739 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
743 // Regenerate audio format and copy format for the Output AU
746 ret = AUGraphUpdate(m_audioGraph, NULL);
749 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
753 std::string formatString;
754 AudioStreamBasicDescription inputDesc_end, outputDesc_end;
755 m_audioUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
756 m_audioUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kInputBus);
757 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
758 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
762 m_mixerUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
763 m_mixerUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
764 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
765 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
770 m_inputUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
771 m_inputUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
772 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
773 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
776 ret = AUGraphInitialize(m_audioGraph);
779 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
783 SetCurrentVolume(initVolume);
785 SetInputSource(pSource);
792 bool CCoreAudioGraph::Close()
801 SetInputSource(NULL);
803 while (!m_auUnitList.empty())
805 CAUOutputDevice *d = m_auUnitList.front();
806 m_auUnitList.pop_front();
807 ReleaseBus(d->GetBus());
814 ReleaseBus(m_inputUnit->GetBus());
815 m_inputUnit->Close();
822 m_mixerUnit->Close();
829 m_audioUnit->Close();
834 ret = AUGraphUninitialize(m_audioGraph);
837 CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error unitialize. Error = %s", GetError(ret).c_str());
840 ret = AUGraphClose(m_audioGraph);
843 CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error close. Error = %s", GetError(ret).c_str());
846 ret = DisposeAUGraph(m_audioGraph);
849 CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error dispose. Error = %s", GetError(ret).c_str());
855 bool CCoreAudioGraph::Start()
861 Boolean isRunning = false;
863 ret = AUGraphIsRunning(m_audioGraph, &isRunning);
866 CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Audio graph not running. Error = %s", GetError(ret).c_str());
873 m_audioUnit->Start();
875 m_mixerUnit->Start();
877 m_inputUnit->Start();
879 ret = AUGraphStart(m_audioGraph);
882 CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Error starting audio graph. Error = %s", GetError(ret).c_str());
890 bool CCoreAudioGraph::Stop()
896 Boolean isRunning = false;
898 ret = AUGraphIsRunning(m_audioGraph, &isRunning);
901 CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Audio graph not running. Error = %s", GetError(ret).c_str());
914 ret = AUGraphStop(m_audioGraph);
917 CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Error stopping audio graph. Error = %s", GetError(ret).c_str());
924 bool CCoreAudioGraph::SetInputSource(ICoreAudioSource* pSource)
927 return m_inputUnit->SetInputSource(pSource);
928 else if (m_audioUnit)
929 return m_audioUnit->SetInputSource(pSource);
934 bool CCoreAudioGraph::SetCurrentVolume(Float32 vol)
939 return m_mixerUnit->SetCurrentVolume(vol);
942 CAUOutputDevice *CCoreAudioGraph::DestroyUnit(CAUOutputDevice *outputUnit)
949 for (AUUnitList::iterator itt = m_auUnitList.begin(); itt != m_auUnitList.end(); ++itt)
950 if (*itt == outputUnit)
952 m_auUnitList.erase(itt);
956 ReleaseBus(outputUnit->GetBus());
960 AUGraphUpdate(m_audioGraph, NULL);
962 printf("Remove unit\n\n");
971 CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
973 if (!m_audioUnit || !m_mixerUnit)
976 std::string formatString;
977 AudioStreamBasicDescription inputFormat;
978 AudioStreamBasicDescription outputFormat;
982 int busNumber = GetFreeBus();
983 if (busNumber == INVALID_BUS)
986 // create output unit
987 CAUOutputDevice *outputUnit = new CAUOutputDevice();
988 if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
991 outputUnit->SetMaxFramesPerSlice(m_audioUnit->GetBufferFrameSize());
993 m_audioUnit->GetFormatDesc(format, &inputFormat);
995 // get the format frm the mixer
996 if (!m_mixerUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
999 if (!outputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
1002 if (!outputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
1005 ret = AUGraphConnectNodeInput(m_audioGraph, outputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
1008 CLog::Log(LOGERROR, "CCoreAudioGraph::CreateUnit: Error connecting outputUnit. Error = %s", GetError(ret).c_str());
1012 // TODO: setup mixmap, get free bus number for connection
1014 outputUnit->SetBus(busNumber);
1016 AUGraphUpdate(m_audioGraph, NULL);
1018 printf("Add unit\n\n");
1022 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputFormat, formatString));
1023 CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputFormat, formatString));
1025 m_auUnitList.push_back(outputUnit);
1034 int CCoreAudioGraph::GetFreeBus()
1036 for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
1038 if (!m_reservedBusNumber[i])
1040 m_reservedBusNumber[i] = true;
1047 void CCoreAudioGraph::ReleaseBus(int busNumber)
1049 if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
1052 m_reservedBusNumber[busNumber] = false;
1055 bool CCoreAudioGraph::IsBusFree(int busNumber)
1057 if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
1059 return m_reservedBusNumber[busNumber];
1062 int CCoreAudioGraph::GetMixerChannelOffset(int busNumber)
1068 AudioStreamBasicDescription fmt;
1070 for (int i = 0; i < busNumber; i++)
1072 memset(&fmt, 0x0, sizeof(fmt));
1073 m_mixerUnit->GetFormat(&fmt, kAudioUnitScope_Input, busNumber);
1074 offset += fmt.mChannelsPerFrame;
1079 void CCoreAudioGraph::ShowGraph()
1081 CAShow(m_audioGraph);
1084 float CCoreAudioGraph::GetLatency()
1089 delay += m_audioUnit->GetLatency();
1094 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1095 // CCoreAudioAEHALIOS
1096 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1097 CCoreAudioAEHALIOS::CCoreAudioAEHALIOS() :
1098 m_audioGraph (NULL ),
1099 m_Initialized (false ),
1100 m_Passthrough (false ),
1101 m_allowMixing (false ),
1103 m_initVolume (1.0f ),
1104 m_NumLatencyFrames (0 ),
1105 m_OutputBufferIndex (0 ),
1110 CCoreAudioAEHALIOS::~CCoreAudioAEHALIOS()
1114 delete m_audioGraph;
1117 bool CCoreAudioAEHALIOS::InitializePCM(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing)
1122 m_audioGraph->Close();
1123 delete m_audioGraph;
1125 m_audioGraph = new CCoreAudioGraph();
1130 if (!m_audioGraph->Open(pSource, format, allowMixing, m_initVolume))
1132 CLog::Log(LOGERROR, "CCoreAudioAEHALIOS::Initialize: Unable to initialize audio due a missconfiguration. Try 2.0 speaker configuration.");
1136 m_NumLatencyFrames = 0;
1138 m_allowMixing = allowMixing;
1143 bool CCoreAudioAEHALIOS::InitializePCMEncoded(ICoreAudioSource *pSource, AEAudioFormat &format)
1145 if (!InitializePCM(pSource, format, false))
1151 bool CCoreAudioAEHALIOS::Initialize(ICoreAudioSource *ae, bool passThrough, AEAudioFormat &format, AEDataFormat rawDataFormat, std::string &device, float initVolume)
1153 m_ae = (CCoreAudioAE *)ae;
1158 m_initformat = format;
1159 m_Passthrough = passThrough;
1161 m_OutputBufferIndex = 0;
1162 m_rawDataFormat = rawDataFormat;
1163 m_initVolume = initVolume;
1165 if (format.m_channelLayout.Count() == 0)
1167 CLog::Log(LOGERROR, "CCoreAudioAEHALIOS::Initialize - Unable to open the requested channel layout");
1171 if (device.find("CoreAudio:"))
1172 device.erase(0, strlen("CoreAudio:"));
1174 // If this is a passthrough (AC3/DTS) stream, attempt to handle it natively
1180 // If this is a PCM stream, or we failed to handle a passthrough stream natively,
1181 // prepare the standard interleaved PCM interface
1184 // If we are here and this is a passthrough stream, native handling failed.
1185 // Try to handle it as IEC61937 data over straight PCM (DD-Wav)
1186 bool configured = false;
1189 CLog::Log(LOGERROR, "CCoreAudioAEHALIOS::Initialize: No suitable AC3 output format found. Attempting DD-Wav.");
1190 configured = InitializePCMEncoded(ae, format);
1194 // Standard PCM data
1195 configured = InitializePCM(ae, format, true);
1198 if (!configured) // No suitable output format was able to be configured
1203 m_audioGraph->ShowGraph();
1205 m_Initialized = true;
1210 CAUOutputDevice *CCoreAudioAEHALIOS::DestroyUnit(CAUOutputDevice *outputUnit)
1212 if (m_audioGraph && outputUnit)
1213 return m_audioGraph->DestroyUnit(outputUnit);
1218 CAUOutputDevice *CCoreAudioAEHALIOS::CreateUnit(ICoreAudioSource *pSource, AEAudioFormat &format)
1220 CAUOutputDevice *outputUnit = NULL;
1222 // when HAL is using a mixer, the input is routed through converter units.
1223 // therefore we create a converter unit attach the source and give it back.
1224 if (m_allowMixing && m_audioGraph)
1226 outputUnit = m_audioGraph->CreateUnit(format);
1228 if (pSource && outputUnit)
1229 outputUnit->SetInputSource(pSource);
1235 void CCoreAudioAEHALIOS::Deinitialize()
1245 m_audioGraph->SetInputSource(NULL);
1249 //m_audioGraph->Close();
1250 delete m_audioGraph;
1252 m_audioGraph = NULL;
1254 m_NumLatencyFrames = 0;
1255 m_OutputBufferIndex = 0;
1257 m_Initialized = false;
1258 m_Passthrough = false;
1261 void CCoreAudioAEHALIOS::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
1263 IOSCoreAudioDeviceList deviceList;
1264 CIOSCoreAudioHardware::GetOutputDevices(&deviceList);
1266 // Add default output device if GetOutputDevices return nothing
1267 devices.push_back(AEDevice("Default", "IOSCoreAudio:default"));
1269 std::string deviceName;
1270 for (int i = 0; !deviceList.empty(); i++)
1272 std::string deviceName_Internal = std::string("IOSCoreAudio:") + deviceName;
1273 devices.push_back(AEDevice(deviceName, deviceName_Internal));
1275 deviceList.pop_front();
1280 void CCoreAudioAEHALIOS::Stop()
1285 m_audioGraph->Stop();
1288 bool CCoreAudioAEHALIOS::Start()
1293 m_audioGraph->Start();
1298 void CCoreAudioAEHALIOS::SetDirectInput(ICoreAudioSource *pSource, AEAudioFormat &format)
1303 // when HAL is initialized encoded we use directIO
1304 // when HAL is not in encoded mode and there is no mixer attach source the audio unit
1305 // when mixing is allowed in HAL, HAL is working with converter units where we attach the source.
1307 if (!m_encoded && !m_allowMixing)
1309 // register render callback for the audio unit
1310 m_audioGraph->SetInputSource(pSource);
1314 m_audioGraph->ShowGraph();
1318 double CCoreAudioAEHALIOS::GetDelay()
1323 delay = (float)(m_NumLatencyFrames) / (m_initformat.m_sampleRate);
1328 return (double)(BUFFERED_FRAMES) / (double)(m_initformat.m_sampleRate);
1331 void CCoreAudioAEHALIOS::SetVolume(float volume)
1334 m_audioGraph->SetCurrentVolume(volume);
1337 unsigned int CCoreAudioAEHALIOS::GetBufferIndex()
1339 return m_OutputBufferIndex;