changed: Improve (fallback) logic for subtitle downloading
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / CoreAudioAEHALIOS.cpp
1 /*
2  *      Copyright (C) 2011-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #if defined(TARGET_DARWIN_IOS)
22 #include "system.h"
23
24 #include "CoreAudioAEHALIOS.h"
25
26 #include "xbmc/cores/AudioEngine/Utils/AEUtil.h"
27 #include "AEFactory.h"
28 #include "CoreAudioAE.h"
29 #include "utils/log.h"
30
31 #include <math.h>
32
33 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
34
35 // use the maximum frames per slice allows audio play when the screen is locked
36 #define BUFFERED_FRAMES 4096
37
38 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
39 // CIOSCoreAudioHardware
40 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
41
42 AudioComponentInstance CIOSCoreAudioHardware::FindAudioDevice(std::string searchName)
43 {
44   if (!searchName.length())
45     return 0;
46
47   AudioComponentInstance defaultDevice = GetDefaultOutputDevice();
48
49   return defaultDevice;
50 }
51
52 AudioComponentInstance CIOSCoreAudioHardware::GetDefaultOutputDevice()
53 {
54   AudioComponentInstance ret = (AudioComponentInstance)1;
55
56   return ret;
57 }
58
59 UInt32 CIOSCoreAudioHardware::GetOutputDevices(IOSCoreAudioDeviceList* pList)
60 {
61   if (!pList)
62     return 0;
63
64   return 0;
65 }
66
67 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
68 // CCoreAudioUnit
69 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
70 CCoreAudioUnit::CCoreAudioUnit() :
71 m_pSource         (NULL         ),
72 m_audioUnit       (NULL         ),
73 m_audioNode       (NULL         ),
74 m_audioGraph      (NULL         ),
75 m_Initialized     (false        ),
76 m_renderProc      (NULL         ),
77 m_busNumber       (INVALID_BUS  )
78 {
79 }
80
81 CCoreAudioUnit::~CCoreAudioUnit()
82 {
83   Close();
84 }
85
86 bool CCoreAudioUnit::Open(AUGraph audioGraph, AudioComponentDescription desc)
87 {
88   if (m_audioUnit)
89     Close();
90
91   OSStatus ret;
92
93   m_Initialized = false;
94
95   ret = AUGraphAddNode(audioGraph, &desc, &m_audioNode);
96   if (ret)
97   {
98     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error add m_outputNode. Error = %s", GetError(ret).c_str());
99     return false;
100   }
101
102   ret = AUGraphNodeInfo(audioGraph, m_audioNode, NULL, &m_audioUnit);
103   if (ret)
104   {
105     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error getting m_outputNode. Error = %s", GetError(ret).c_str());
106     return false;
107   }
108
109   m_audioGraph  = audioGraph;
110   m_Initialized = true;
111
112   Start();
113
114   return true;
115 }
116
117 bool CCoreAudioUnit::Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer)
118 {
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);
126 }
127
128 void CCoreAudioUnit::Close()
129 {
130   if (!m_Initialized && !m_audioUnit)
131     return;
132
133   if (m_renderProc)
134     SetInputSource(NULL);
135
136   Stop();
137
138   if (m_busNumber != INVALID_BUS)
139   {
140     OSStatus ret = AUGraphDisconnectNodeInput(m_audioGraph, m_audioNode, m_busNumber);
141     if (ret)
142     {
143       CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
144     }
145
146     ret = AUGraphRemoveNode(m_audioGraph, m_audioNode);
147     if (ret)
148     {
149       CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
150     }
151   }
152
153   AUGraphUpdate(m_audioGraph, NULL);
154
155   m_Initialized = false;
156   m_audioUnit = NULL;
157   m_audioNode = NULL;
158   m_pSource = NULL;
159 }
160
161 bool CCoreAudioUnit::GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
162 {
163   if (!m_audioUnit || !pDesc)
164     return false;
165
166   UInt32 size = sizeof(AudioStreamBasicDescription);
167   OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, &size);
168   if (ret)
169   {
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());
171     return false;
172   }
173   return true;
174 }
175
176 bool CCoreAudioUnit::SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
177 {
178   if (!m_audioUnit || !pDesc)
179     return false;
180
181   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, sizeof(AudioStreamBasicDescription));
182   if (ret)
183   {
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());
185     return false;
186   }
187   return true;
188 }
189
190 bool CCoreAudioUnit::SetMaxFramesPerSlice(UInt32 maxFrames)
191 {
192   if (!m_audioUnit)
193     return false;
194
195   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(UInt32));
196   if (ret)
197   {
198     CLog::Log(LOGERROR, "CCoreAudioUnit::SetMaxFramesPerSlice: Unable to set AudioUnit max frames per slice. Error = %s", GetError(ret).c_str());
199     return false;
200   }
201   return true;
202 }
203
204 bool CCoreAudioUnit::SetInputSource(ICoreAudioSource* pSource)
205 {
206   m_pSource = pSource;
207   if (pSource)
208     return SetRenderProc();
209   else
210     return RemoveRenderProc();
211 }
212
213 bool CCoreAudioUnit::SetRenderProc()
214 {
215   if (!m_audioUnit || m_renderProc)
216     return false;
217
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));
223   if (ret)
224   {
225     CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: Unable to set AudioUnit render callback. Error = %s", GetError(ret).c_str());
226     return false;
227   }
228
229   m_renderProc = RenderCallback;
230
231   return true;
232 }
233
234 bool CCoreAudioUnit::RemoveRenderProc()
235 {
236   if (!m_audioUnit || !m_renderProc)
237     return false;
238
239
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));
245   if (ret)
246   {
247     CLog::Log(LOGERROR, "CCoreAudioUnit::RemoveRenderProc: Unable to remove AudioUnit render callback. Error = %s", GetError(ret).c_str());
248     return false;
249   }
250
251   m_renderProc = NULL;
252
253   Sleep(100);
254
255   return true;
256 }
257
258 OSStatus CCoreAudioUnit::RenderCallback(void *inRefCon,
259                                         AudioUnitRenderActionFlags *ioActionFlags,
260                                         const AudioTimeStamp *inTimeStamp,
261                                         UInt32 inBusNumber,
262                                         UInt32 inNumberFrames,
263                                         AudioBufferList *ioData)
264 {
265   OSStatus ret = noErr;
266   CCoreAudioUnit *audioUnit = (CCoreAudioUnit*)inRefCon;
267
268   if (audioUnit->m_pSource)
269   {
270     ret = audioUnit->m_pSource->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
271   }
272   else
273   {
274     ioData->mBuffers[0].mDataByteSize = 0;
275     if (ioActionFlags)
276       *ioActionFlags |=  kAudioUnitRenderAction_OutputIsSilence;
277   }
278
279
280   return ret;
281 }
282
283 void CCoreAudioUnit::GetFormatDesc(AEAudioFormat format,
284                                    AudioStreamBasicDescription *streamDesc)
285 {
286   unsigned int bps = CAEUtil::DataFormatToBits(format.m_dataFormat);
287
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)
294   {
295     case AE_FMT_FLOAT:
296       streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
297       streamDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
298       break;
299     case AE_FMT_S16NE:
300     case AE_FMT_AC3:
301     case AE_FMT_DTS:
302     case AE_FMT_DTSHD:
303     case AE_FMT_TRUEHD:
304     case AE_FMT_EAC3:
305       streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
306       streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
307       break;
308     case AE_FMT_S16LE:
309       streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
310       break;
311     case AE_FMT_S16BE:
312       streamDesc->mFormatFlags |= kAudioFormatFlagIsBigEndian;
313       streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
314       break;
315     default:
316       streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
317       streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
318       break;
319   }
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;
327 }
328
329 float CCoreAudioUnit::GetLatency()
330 {
331   if (!m_audioUnit)
332     return 0.0f;
333
334   //kAudioSessionProperty_CurrentHardwareIOBufferDuration
335   //kAudioSessionProperty_CurrentHardwareOutputLatency
336
337   Float32 preferredBufferSize = 0.0f;
338   UInt32 size = sizeof(preferredBufferSize);
339   AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency, &size, &preferredBufferSize);
340   return preferredBufferSize;
341 }
342
343 bool CCoreAudioUnit::SetSampleRate(Float64 sampleRate, AudioUnitScope scope, AudioUnitElement bus)
344 {
345   if (!m_audioUnit)
346     return false;
347
348   UInt32 size = sizeof(Float64);
349
350   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SampleRate, scope, bus, &sampleRate, size);
351   if (ret)
352   {
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());
354     return false;
355   }
356   return true;
357 }
358
359 bool CCoreAudioUnit::Stop()
360 {
361   if (!m_audioUnit)
362     return false;
363
364   AudioOutputUnitStop(m_audioUnit);
365
366   return true;
367 }
368
369 bool CCoreAudioUnit::Start()
370 {
371   if (!m_audioUnit)
372     return false;
373
374   AudioOutputUnitStart(m_audioUnit);
375
376   return true;
377 }
378
379 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
380 // CAUOutputDevice
381 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
382 CAUOutputDevice::CAUOutputDevice()
383 {
384 }
385
386 CAUOutputDevice::~CAUOutputDevice()
387 {
388 }
389
390 /*
391 Float32 CAUOutputDevice::GetCurrentVolume()
392 {
393   if (!m_audioUnit)
394     return 0.0f;
395
396   Float32 volPct = 0.0f;
397   OSStatus ret = AudioUnitGetParameter(m_audioUnit,  kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &volPct);
398   if (ret)
399   {
400     CLog::Log(LOGERROR, "CCoreAudioUnit::GetCurrentVolume: Unable to get AudioUnit volume. Error = %s", GetError(ret).c_str());
401     return 0.0f;
402   }
403   return volPct;
404 }
405
406 bool CAUOutputDevice::SetCurrentVolume(Float32 vol)
407 {
408   if (!m_audioUnit)
409     return false;
410
411   OSStatus ret = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume,
412                                        kAudioUnitScope_Global, 0, vol, 0);
413   if (ret)
414   {
415     CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentVolume: Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
416     return false;
417   }
418   return true;
419 }
420 */
421
422 UInt32 CAUOutputDevice::GetBufferFrameSize()
423 {
424   if (!m_audioUnit)
425     return 0;
426
427   return BUFFERED_FRAMES;
428 }
429
430 bool CAUOutputDevice::EnableInputOuput()
431 {
432   if (!m_audioUnit)
433     return false;
434
435   OSStatus ret;
436   UInt32 enable;
437   UInt32 hasio = 0;
438   UInt32 size=sizeof(UInt32);
439
440   ret = AudioUnitGetProperty(m_audioUnit,kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
441
442   if (!ret && hasio)
443   {
444     enable = 1;
445     ret =  AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enable, sizeof(enable));
446     if (ret)
447     {
448       CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to enable input on bus 1. Error = %s", GetError(ret).c_str());
449       return false;
450     }
451
452     enable = 1;
453     ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &enable, sizeof(enable));
454     if (ret)
455     {
456       CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to disable output on bus 0. Error = %s", GetError(ret).c_str());
457       return false;
458     }
459   }
460
461   return true;
462 }
463
464 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
465 // CAUMultiChannelMixer
466 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
467 CAUMultiChannelMixer::CAUMultiChannelMixer()
468 {
469 }
470
471 CAUMultiChannelMixer::~CAUMultiChannelMixer()
472 {
473
474 }
475
476 UInt32 CAUMultiChannelMixer::GetInputBusCount()
477 {
478   if (!m_audioUnit)
479     return 0;
480
481   UInt32 busCount = 0;
482   UInt32 size = sizeof(busCount);
483   OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, &size);
484   if (ret)
485   {
486     CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetInputBusCount: Unable to get input bus count. Error = %s", GetError(ret).c_str());
487     return 0;
488   }
489   return busCount;
490 }
491
492 bool CAUMultiChannelMixer::SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat)
493 {
494   if (!m_audioUnit)
495     return false;
496
497   for (UInt32 i = 0; i < busCount; i++)
498   {
499     if (!SetFormat(pFormat, kAudioUnitScope_Input, i))
500       return false;
501   }
502
503   return true;
504 }
505
506 bool CAUMultiChannelMixer::SetInputBusCount(UInt32 busCount)
507 {
508   if (!m_audioUnit)
509     return false;
510
511   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(UInt32));
512   if (ret)
513   {
514     CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetInputBusCount: Unable to set input bus count. Error = %s", GetError(ret).c_str());
515     return false;
516   }
517   return true;
518 }
519
520 UInt32 CAUMultiChannelMixer::GetOutputBusCount()
521 {
522   if (!m_audioUnit)
523     return 0;
524
525   UInt32 busCount = 0;
526   UInt32 size = sizeof(busCount);
527   OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, &size);
528   if (ret)
529   {
530     CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetOutputBusCount: Unable to get output bus count. Error = %s", GetError(ret).c_str());
531     return 0;
532   }
533   return busCount;
534 }
535
536 bool CAUMultiChannelMixer::SetOutputBusCount(UInt32 busCount)
537 {
538   if (!m_audioUnit)
539     return false;
540
541   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, sizeof(UInt32));
542   if (ret)
543   {
544     CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetOutputBusCount: Unable to set output bus count. Error = %s", GetError(ret).c_str());
545     return false;
546   }
547   return true;
548 }
549
550 Float32 CAUMultiChannelMixer::GetCurrentVolume()
551 {
552
553   if (!m_audioUnit)
554     return false;
555
556   Float32 volPct = 0.0f;
557   OSStatus ret = AudioUnitGetParameter(m_audioUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, kInputBus, &volPct);
558   if (ret)
559   {
560     CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetCurrentVolume: Unable to get Mixer volume. Error = %s", GetError(ret).c_str());
561     return 0.0f;
562   }
563   return volPct;
564
565 }
566
567 bool CAUMultiChannelMixer::SetCurrentVolume(Float32 vol)
568 {
569
570   if (!m_audioUnit)
571     return false;
572
573   OSStatus ret = AudioUnitSetParameter(m_audioUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, kOutputBus, vol, 0);
574   if (ret)
575   {
576     CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetCurrentVolume: Unable to set Mixer volume. Error = %s", GetError(ret).c_str());
577     return false;
578   }
579
580   return true;
581 }
582
583 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
584 // CCoreAudioGraph
585 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
586 CCoreAudioGraph::CCoreAudioGraph() :
587 m_audioGraph    (NULL ),
588 m_audioUnit     (NULL ),
589 m_mixerUnit     (NULL ),
590 m_inputUnit     (NULL ),
591 m_initialized   (false),
592 m_allowMixing   (false)
593 {
594   for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
595   {
596     m_reservedBusNumber[i] = false;
597   }
598 }
599
600 CCoreAudioGraph::~CCoreAudioGraph()
601 {
602   Close();
603 }
604
605 bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing, float initVolume)
606 {
607   OSStatus ret;
608
609   AudioStreamBasicDescription inputFormat;
610   AudioStreamBasicDescription outputFormat;
611
612   m_allowMixing   = allowMixing;
613
614   ret = NewAUGraph(&m_audioGraph);
615   if (ret)
616   {
617     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error create audio grpah. Error = %s", GetError(ret).c_str());
618     return false;
619   }
620   ret = AUGraphOpen(m_audioGraph);
621   if (ret)
622   {
623     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error open audio grpah. Error = %s", GetError(ret).c_str());
624     return false;
625   }
626
627   // get output unit
628   if (m_audioUnit)
629   {
630     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error audio unit already open. double call ?");
631     return false;
632   }
633
634   m_audioUnit = new CAUOutputDevice();
635   if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple))
636     return false;
637
638   UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
639
640   if (!m_audioUnit->EnableInputOuput())
641     return false;
642
643   m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
644
645   m_audioUnit->GetFormatDesc(format, &inputFormat);
646
647   //if(!allowMixing)
648   //{
649     if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
650       return false;
651
652     if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus))
653       return false;
654   //}
655
656   if (allowMixing)
657   {
658     // get mixer unit
659     if (m_mixerUnit)
660     {
661       CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
662       return false;
663     }
664
665     m_mixerUnit = new CAUMultiChannelMixer();
666
667     if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MultiChannelMixer, kAudioUnitManufacturer_Apple))
668       return false;
669
670     m_mixerUnit->SetMaxFramesPerSlice(bufferFrames);
671
672     // set number of input buses
673     if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT))
674       return false;
675
676     //if(!m_mixerUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus))
677     //  return false;
678
679     m_mixerUnit->SetBus(0);
680
681     if (!m_audioUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
682       return false;
683
684     /*
685     if(!m_mixerUnit->SetInputBusFormat(MAX_CONNECTION_LIMIT, &outputFormat))
686       return false;
687     */
688
689     ret =  AUGraphConnectNodeInput(m_audioGraph, m_mixerUnit->GetNode(), 0, m_audioUnit->GetNode(), 0);
690     if (ret)
691     {
692       CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_m_mixerNode. Error = %s", GetError(ret).c_str());
693       return false;
694     }
695
696     // get output unit
697     if (m_inputUnit)
698     {
699       CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
700       return false;
701     }
702
703     m_inputUnit = new CAUOutputDevice();
704
705     if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
706       return false;
707
708     m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
709
710     if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
711       return false;
712
713     /*
714     if(!m_inputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
715       return false;
716     */
717
718     // configure output unit
719     int busNumber = GetFreeBus();
720
721     ret = AUGraphConnectNodeInput(m_audioGraph, m_inputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
722     if (ret)
723     {
724       CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_converterNode. Error = %s", GetError(ret).c_str());
725       return false;
726     }
727
728     m_inputUnit->SetBus(busNumber);
729
730     ret = AUGraphUpdate(m_audioGraph, NULL);
731     if (ret)
732     {
733       CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
734       return false;
735     }
736     ret = AUGraphInitialize(m_audioGraph);
737     if (ret)
738     {
739       CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
740       return false;
741     }
742
743     // Regenerate audio format and copy format for the Output AU
744   }
745
746   ret = AUGraphUpdate(m_audioGraph, NULL);
747   if (ret)
748   {
749     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
750     return false;
751   }
752
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));
759
760   if (m_mixerUnit)
761   {
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));
766   }
767
768   if (m_inputUnit)
769   {
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));
774   }
775
776   ret = AUGraphInitialize(m_audioGraph);
777   if (ret)
778   {
779     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
780     return false;
781   }
782
783   SetCurrentVolume(initVolume);
784
785   SetInputSource(pSource);
786
787   ShowGraph();
788
789   return Start();
790 }
791
792 bool CCoreAudioGraph::Close()
793 {
794   if (!m_audioGraph)
795     return false;
796
797   OSStatus ret;
798
799   Stop();
800
801   SetInputSource(NULL);
802
803   while (!m_auUnitList.empty())
804   {
805     CAUOutputDevice *d = m_auUnitList.front();
806     m_auUnitList.pop_front();
807     ReleaseBus(d->GetBus());
808     d->Close();
809     delete d;
810   }
811
812   if (m_inputUnit)
813   {
814     ReleaseBus(m_inputUnit->GetBus());
815     m_inputUnit->Close();
816     delete m_inputUnit;
817     m_inputUnit = NULL;
818   }
819
820   if (m_mixerUnit)
821   {
822     m_mixerUnit->Close();
823     delete m_mixerUnit;
824     m_mixerUnit = NULL;
825   }
826
827   if (m_audioUnit)
828   {
829     m_audioUnit->Close();
830     delete m_audioUnit;
831     m_audioUnit = NULL;
832   }
833
834   ret = AUGraphUninitialize(m_audioGraph);
835   if (ret)
836   {
837     CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error unitialize. Error = %s", GetError(ret).c_str());
838   }
839
840   ret = AUGraphClose(m_audioGraph);
841   if (ret)
842   {
843     CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error close. Error = %s", GetError(ret).c_str());
844   }
845
846   ret = DisposeAUGraph(m_audioGraph);
847   if (ret)
848   {
849     CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error dispose. Error = %s", GetError(ret).c_str());
850   }
851
852   return true;
853 }
854
855 bool CCoreAudioGraph::Start()
856 {
857   if (!m_audioGraph)
858     return false;
859
860   OSStatus ret;
861   Boolean isRunning = false;
862
863   ret = AUGraphIsRunning(m_audioGraph, &isRunning);
864   if (ret)
865   {
866     CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Audio graph not running. Error = %s", GetError(ret).c_str());
867     return false;
868   }
869   if (!isRunning)
870   {
871
872     if (m_audioUnit)
873       m_audioUnit->Start();
874     if (m_mixerUnit)
875       m_mixerUnit->Start();
876     if (m_inputUnit)
877       m_inputUnit->Start();
878
879     ret = AUGraphStart(m_audioGraph);
880     if (ret)
881     {
882       CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Error starting audio graph. Error = %s", GetError(ret).c_str());
883     }
884     ShowGraph();
885   }
886
887   return true;
888 }
889
890 bool CCoreAudioGraph::Stop()
891 {
892   if (!m_audioGraph)
893     return false;
894
895   OSStatus ret;
896   Boolean isRunning = false;
897
898   ret = AUGraphIsRunning(m_audioGraph, &isRunning);
899   if (ret)
900   {
901     CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Audio graph not running. Error = %s", GetError(ret).c_str());
902     return false;
903   }
904   if (isRunning)
905   {
906
907     if (m_inputUnit)
908       m_inputUnit->Stop();
909     if (m_mixerUnit)
910       m_mixerUnit->Stop();
911     if (m_audioUnit)
912       m_audioUnit->Stop();
913
914     ret = AUGraphStop(m_audioGraph);
915     if (ret)
916     {
917       CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Error stopping audio graph. Error = %s", GetError(ret).c_str());
918     }
919   }
920
921   return true;
922 }
923
924 bool CCoreAudioGraph::SetInputSource(ICoreAudioSource* pSource)
925 {
926   if (m_inputUnit)
927     return m_inputUnit->SetInputSource(pSource);
928   else if (m_audioUnit)
929     return m_audioUnit->SetInputSource(pSource);
930
931   return false;
932 }
933
934 bool CCoreAudioGraph::SetCurrentVolume(Float32 vol)
935 {
936   if (!m_mixerUnit)
937     return false;
938
939   return m_mixerUnit->SetCurrentVolume(vol);
940 }
941
942 CAUOutputDevice *CCoreAudioGraph::DestroyUnit(CAUOutputDevice *outputUnit)
943 {
944   if (!outputUnit)
945     return NULL;
946
947   Stop();
948
949   for (AUUnitList::iterator itt = m_auUnitList.begin(); itt != m_auUnitList.end(); ++itt)
950     if (*itt == outputUnit)
951     {
952       m_auUnitList.erase(itt);
953       break;
954     }
955
956   ReleaseBus(outputUnit->GetBus());
957   outputUnit->Close();
958   delete outputUnit;
959
960   AUGraphUpdate(m_audioGraph, NULL);
961
962   printf("Remove unit\n\n");
963   ShowGraph();
964   printf("\n");
965
966   Start();
967
968   return NULL;
969 }
970
971 CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
972 {
973   if (!m_audioUnit || !m_mixerUnit)
974     return NULL;
975
976   std::string formatString;
977   AudioStreamBasicDescription inputFormat;
978   AudioStreamBasicDescription outputFormat;
979
980   OSStatus ret;
981
982   int busNumber = GetFreeBus();
983   if (busNumber == INVALID_BUS)
984     return  NULL;
985
986   // create output unit
987   CAUOutputDevice *outputUnit = new CAUOutputDevice();
988   if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
989     goto error;
990   
991   outputUnit->SetMaxFramesPerSlice(m_audioUnit->GetBufferFrameSize());
992
993   m_audioUnit->GetFormatDesc(format, &inputFormat);
994
995   // get the format frm the mixer
996   if (!m_mixerUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
997     goto error;
998
999   if (!outputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
1000     goto error;
1001
1002   if (!outputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
1003     goto error;
1004
1005   ret = AUGraphConnectNodeInput(m_audioGraph, outputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
1006   if (ret)
1007   {
1008     CLog::Log(LOGERROR, "CCoreAudioGraph::CreateUnit: Error connecting outputUnit. Error = %s", GetError(ret).c_str());
1009     goto error;
1010   }
1011
1012   // TODO: setup mixmap, get free bus number for connection
1013
1014   outputUnit->SetBus(busNumber);
1015
1016   AUGraphUpdate(m_audioGraph, NULL);
1017
1018   printf("Add unit\n\n");
1019   ShowGraph();
1020   printf("\n");
1021
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));
1024
1025   m_auUnitList.push_back(outputUnit);
1026
1027   return outputUnit;
1028
1029 error:
1030   delete outputUnit;
1031   return NULL;
1032 }
1033
1034 int CCoreAudioGraph::GetFreeBus()
1035 {
1036   for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
1037   {
1038     if (!m_reservedBusNumber[i])
1039     {
1040       m_reservedBusNumber[i] = true;
1041       return i;
1042     }
1043   }
1044   return INVALID_BUS;
1045 }
1046
1047 void CCoreAudioGraph::ReleaseBus(int busNumber)
1048 {
1049   if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
1050     return;
1051
1052   m_reservedBusNumber[busNumber] = false;
1053 }
1054
1055 bool CCoreAudioGraph::IsBusFree(int busNumber)
1056 {
1057   if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
1058     return false;
1059   return m_reservedBusNumber[busNumber];
1060 }
1061
1062 int CCoreAudioGraph::GetMixerChannelOffset(int busNumber)
1063 {
1064   if (!m_mixerUnit)
1065     return 0;
1066
1067   int offset = 0;
1068   AudioStreamBasicDescription fmt;
1069
1070   for (int i = 0; i < busNumber; i++)
1071   {
1072     memset(&fmt, 0x0, sizeof(fmt));
1073     m_mixerUnit->GetFormat(&fmt, kAudioUnitScope_Input, busNumber);
1074     offset += fmt.mChannelsPerFrame;
1075   }
1076   return offset;
1077 }
1078
1079 void CCoreAudioGraph::ShowGraph()
1080 {
1081   CAShow(m_audioGraph);
1082 }
1083
1084 float CCoreAudioGraph::GetLatency()
1085 {
1086   float delay = 0.0f;
1087
1088   if (m_audioUnit)
1089     delay += m_audioUnit->GetLatency();
1090
1091   return delay;
1092 }
1093
1094 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1095 // CCoreAudioAEHALIOS
1096 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1097 CCoreAudioAEHALIOS::CCoreAudioAEHALIOS() :
1098 m_audioGraph        (NULL   ),
1099 m_Initialized       (false  ),
1100 m_Passthrough       (false  ),
1101 m_allowMixing       (false  ),
1102 m_encoded           (false  ),
1103 m_initVolume        (1.0f   ),
1104 m_NumLatencyFrames  (0      ),
1105 m_OutputBufferIndex (0      ),
1106 m_ae                (NULL   )
1107 {
1108 }
1109
1110 CCoreAudioAEHALIOS::~CCoreAudioAEHALIOS()
1111 {
1112   Deinitialize();
1113
1114   delete m_audioGraph;
1115 }
1116
1117 bool CCoreAudioAEHALIOS::InitializePCM(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing)
1118 {
1119
1120   if (m_audioGraph)
1121   {
1122     m_audioGraph->Close();
1123     delete m_audioGraph;
1124   }
1125   m_audioGraph = new CCoreAudioGraph();
1126
1127   if (!m_audioGraph)
1128     return false;
1129
1130   if (!m_audioGraph->Open(pSource, format, allowMixing, m_initVolume))
1131   {
1132     CLog::Log(LOGERROR, "CCoreAudioAEHALIOS::Initialize: Unable to initialize audio due a missconfiguration. Try 2.0 speaker configuration.");
1133     return false;
1134   }
1135
1136   m_NumLatencyFrames = 0;
1137
1138   m_allowMixing = allowMixing;
1139
1140   return true;
1141 }
1142
1143 bool CCoreAudioAEHALIOS::InitializePCMEncoded(ICoreAudioSource *pSource, AEAudioFormat &format)
1144 {
1145   if (!InitializePCM(pSource, format, false))
1146     return false;
1147
1148   return true;
1149 }
1150
1151 bool CCoreAudioAEHALIOS::Initialize(ICoreAudioSource *ae, bool passThrough, AEAudioFormat &format, AEDataFormat rawDataFormat, std::string &device, float initVolume)
1152 {
1153   m_ae = (CCoreAudioAE *)ae;
1154
1155   if (!m_ae)
1156     return false;
1157
1158   m_initformat          = format;
1159   m_Passthrough         = passThrough;
1160   m_encoded             = false;
1161   m_OutputBufferIndex   = 0;
1162   m_rawDataFormat       = rawDataFormat;
1163   m_initVolume          = initVolume;
1164
1165   if (format.m_channelLayout.Count() == 0)
1166   {
1167     CLog::Log(LOGERROR, "CCoreAudioAEHALIOS::Initialize - Unable to open the requested channel layout");
1168     return false;
1169   }
1170
1171   if (device.find("CoreAudio:"))
1172     device.erase(0, strlen("CoreAudio:"));
1173
1174   // If this is a passthrough (AC3/DTS) stream, attempt to handle it natively
1175   if (m_Passthrough)
1176   {
1177     m_encoded = false;
1178   }
1179
1180   // If this is a PCM stream, or we failed to handle a passthrough stream natively,
1181   // prepare the standard interleaved PCM interface
1182   if (!m_encoded)
1183   {
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;
1187     if (m_Passthrough)
1188     {
1189       CLog::Log(LOGERROR, "CCoreAudioAEHALIOS::Initialize: No suitable AC3 output format found. Attempting DD-Wav.");
1190       configured = InitializePCMEncoded(ae, format);
1191     }
1192     else
1193     {
1194       // Standard PCM data
1195       configured = InitializePCM(ae, format, true);
1196     }
1197
1198     if (!configured) // No suitable output format was able to be configured
1199       return false;
1200   }
1201
1202   if (m_audioGraph)
1203     m_audioGraph->ShowGraph();
1204
1205   m_Initialized = true;
1206
1207   return true;
1208 }
1209
1210 CAUOutputDevice *CCoreAudioAEHALIOS::DestroyUnit(CAUOutputDevice *outputUnit)
1211 {
1212   if (m_audioGraph && outputUnit)
1213     return m_audioGraph->DestroyUnit(outputUnit);
1214
1215   return NULL;
1216 }
1217
1218 CAUOutputDevice *CCoreAudioAEHALIOS::CreateUnit(ICoreAudioSource *pSource, AEAudioFormat &format)
1219 {
1220   CAUOutputDevice *outputUnit = NULL;
1221
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)
1225   {
1226     outputUnit = m_audioGraph->CreateUnit(format);
1227
1228     if (pSource && outputUnit)
1229       outputUnit->SetInputSource(pSource);
1230   }
1231
1232   return outputUnit;
1233 }
1234
1235 void CCoreAudioAEHALIOS::Deinitialize()
1236 {
1237   if (!m_Initialized)
1238     return;
1239
1240   Stop();
1241
1242   //if (m_encoded)
1243
1244   if (m_audioGraph)
1245     m_audioGraph->SetInputSource(NULL);
1246
1247   if (m_audioGraph)
1248   {
1249     //m_audioGraph->Close();
1250     delete m_audioGraph;
1251   }
1252   m_audioGraph = NULL;
1253
1254   m_NumLatencyFrames = 0;
1255   m_OutputBufferIndex = 0;
1256
1257   m_Initialized = false;
1258   m_Passthrough = false;
1259 }
1260
1261 void CCoreAudioAEHALIOS::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
1262 {
1263   IOSCoreAudioDeviceList deviceList;
1264   CIOSCoreAudioHardware::GetOutputDevices(&deviceList);
1265
1266   // Add default output device if GetOutputDevices return nothing
1267   devices.push_back(AEDevice("Default", "IOSCoreAudio:default"));
1268
1269   std::string deviceName;
1270   for (int i = 0; !deviceList.empty(); i++)
1271   {
1272     std::string deviceName_Internal = std::string("IOSCoreAudio:") + deviceName;
1273     devices.push_back(AEDevice(deviceName, deviceName_Internal));
1274
1275     deviceList.pop_front();
1276
1277   }
1278 }
1279
1280 void CCoreAudioAEHALIOS::Stop()
1281 {
1282   if (!m_Initialized)
1283     return;
1284
1285   m_audioGraph->Stop();
1286 }
1287
1288 bool CCoreAudioAEHALIOS::Start()
1289 {
1290   if (!m_Initialized)
1291     return false;
1292
1293   m_audioGraph->Start();
1294
1295   return true;
1296 }
1297
1298 void CCoreAudioAEHALIOS::SetDirectInput(ICoreAudioSource *pSource, AEAudioFormat &format)
1299 {
1300   if (!m_Initialized)
1301     return;
1302
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.
1306
1307   if (!m_encoded && !m_allowMixing)
1308   {
1309     // register render callback for the audio unit
1310     m_audioGraph->SetInputSource(pSource);
1311   }
1312
1313   if (m_audioGraph)
1314     m_audioGraph->ShowGraph();
1315
1316 }
1317
1318 double CCoreAudioAEHALIOS::GetDelay()
1319 {
1320   /*
1321   float delay;
1322
1323   delay = (float)(m_NumLatencyFrames) / (m_initformat.m_sampleRate);
1324
1325   return delay;
1326   */
1327
1328   return (double)(BUFFERED_FRAMES) / (double)(m_initformat.m_sampleRate);
1329 }
1330
1331 void  CCoreAudioAEHALIOS::SetVolume(float volume)
1332 {
1333   if (!m_encoded)
1334     m_audioGraph->SetCurrentVolume(volume);
1335 }
1336
1337 unsigned int CCoreAudioAEHALIOS::GetBufferIndex()
1338 {
1339   return m_OutputBufferIndex;
1340 }
1341
1342 #endif