changed: Add logic to properly handle subtitles for stacked files
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / CoreAudioUnit.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 #include "CoreAudioUnit.h"
22
23 #include "CoreAudioAEHAL.h"
24 #include "cores/AudioEngine/Utils/AEUtil.h"
25 #include "utils/log.h"
26
27 #include <AudioToolbox/AUGraph.h>
28
29 CCoreAudioUnit::CCoreAudioUnit() :
30   m_pSource         (NULL         ),
31   m_audioUnit       (NULL         ),
32   m_audioNode       (NULL         ),
33   m_audioGraph      (NULL         ),
34   m_Initialized     (false        ),
35   m_renderProc      (NULL         ),
36   m_busNumber       (INVALID_BUS  )
37 {
38 }
39
40 CCoreAudioUnit::~CCoreAudioUnit()
41 {
42   Close();
43 }
44
45 bool CCoreAudioUnit::Open(AUGraph audioGraph, AudioComponentDescription desc)
46 {
47   if (m_audioUnit)
48     Close();
49
50   OSStatus ret;
51
52   m_Initialized = false;
53
54   ret = AUGraphAddNode(audioGraph, &desc, &m_audioNode);
55   if (ret)
56   {
57     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
58       "Error add m_outputNode. Error = %s", GetError(ret).c_str());
59     return false;
60   }
61
62   ret = AUGraphNodeInfo(audioGraph, m_audioNode, 0, &m_audioUnit);
63   if (ret)
64   {
65     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
66       "Error getting m_outputNode. Error = %s", GetError(ret).c_str());
67     return false;
68   }
69
70   m_audioGraph  = audioGraph;
71   m_Initialized = true;
72
73   return true;
74 }
75
76 bool CCoreAudioUnit::Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer)
77 {
78   AudioComponentDescription desc = {0};
79   desc.componentType = type;
80   desc.componentSubType = subType;
81   desc.componentManufacturer = manufacturer;
82
83   return Open(audioGraph, desc);
84 }
85
86 void CCoreAudioUnit::Close()
87 {
88   if (!m_Initialized && !m_audioUnit)
89     return;
90
91   if (m_renderProc)
92     SetInputSource(NULL);
93
94   Stop();
95  
96   if (m_busNumber != INVALID_BUS)
97   {
98     OSStatus ret = AUGraphDisconnectNodeInput(m_audioGraph, m_audioNode, m_busNumber);
99     if (ret && ret != kAUGraphErr_NodeNotFound)
100     {
101       CLog::Log(LOGERROR, "CCoreAudioUnit::Close: "
102         "Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
103     }
104
105     ret = AUGraphRemoveNode(m_audioGraph, m_audioNode);
106     if (ret != noErr)
107     {
108       CLog::Log(LOGERROR, "CCoreAudioUnit::Close: "
109         "Unable to remove AudioUnit. Error = %s", GetError(ret).c_str());
110     }
111   }
112
113   AUGraphUpdate(m_audioGraph, NULL);
114
115   m_Initialized = false;
116   m_audioUnit = NULL;
117   m_audioNode = NULL;
118   m_pSource = NULL;
119 }
120
121 bool CCoreAudioUnit::GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
122 {
123   if (!m_audioUnit || !pDesc)
124     return false;
125
126   UInt32 size = sizeof(AudioStreamBasicDescription);
127   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
128     kAudioUnitProperty_StreamFormat, scope, bus, pDesc, &size);
129   if (ret != noErr)
130   {
131     CLog::Log(LOGERROR, "CCoreAudioUnit::GetFormat: "
132       "Unable to get AudioUnit format. Bus : %d Scope : %d : Error = %s",
133         (int)bus, (int)scope, GetError(ret).c_str());
134     return false;
135   }
136   return true;
137 }
138
139 bool CCoreAudioUnit::SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
140 {
141   if (!m_audioUnit || !pDesc)
142     return false;
143
144   OSStatus ret = AudioUnitSetProperty(m_audioUnit,
145     kAudioUnitProperty_StreamFormat, scope, bus, pDesc, sizeof(AudioStreamBasicDescription));
146   if (ret != noErr)
147   {
148     CLog::Log(LOGERROR, "CCoreAudioUnit::SetFormat: "
149       "Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s",
150        (int)bus, (int)scope, GetError(ret).c_str());
151     return false;
152   }
153   return true;
154 }
155
156 bool CCoreAudioUnit::SetMaxFramesPerSlice(UInt32 maxFrames)
157 {
158   if (!m_audioUnit)
159     return false;
160
161   OSStatus ret = AudioUnitSetProperty(m_audioUnit,
162     kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(UInt32));
163   if (ret != noErr)
164   {
165     CLog::Log(LOGERROR, "CCoreAudioUnit::SetMaxFramesPerSlice: "
166       "Unable to set AudioUnit max frames per slice. Error = %s", GetError(ret).c_str());
167     return false;
168   }
169   return true;
170 }
171
172 bool CCoreAudioUnit::GetSupportedChannelLayouts(AudioChannelLayoutList* pLayouts)
173 {
174   if (!m_audioUnit || !pLayouts)
175     return false;
176
177   UInt32 propSize = 0;
178   Boolean writable = false;
179   OSStatus ret = AudioUnitGetPropertyInfo(m_audioUnit,
180     kAudioUnitProperty_SupportedChannelLayoutTags, kAudioUnitScope_Input, 0, &propSize, &writable);
181   if (ret != noErr)
182   {
183     CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
184       "Unable to retrieve supported channel layout property info. Error = %s", GetError(ret).c_str());
185     return false;
186   }
187   UInt32 layoutCount = propSize / sizeof(AudioChannelLayoutTag);
188   AudioChannelLayoutTag* pSuppLayouts = new AudioChannelLayoutTag[layoutCount];
189   ret = AudioUnitGetProperty(m_audioUnit,
190     kAudioUnitProperty_SupportedChannelLayoutTags, kAudioUnitScope_Output, 0, pSuppLayouts, &propSize);
191   if (ret != noErr)
192   {
193     CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
194       "Unable to retrieve supported channel layouts. Error = %s", GetError(ret).c_str());
195     return false;
196   }
197   for (UInt32 layout = 0; layout < layoutCount; layout++)
198     pLayouts->push_back(pSuppLayouts[layout]);
199   delete[] pSuppLayouts;
200   return true;
201 }
202
203 bool CCoreAudioUnit::SetInputSource(ICoreAudioSource* pSource)
204 {
205   m_pSource = pSource;
206   if (pSource)
207     return SetRenderProc();
208   else
209     return RemoveRenderProc();
210 }
211
212 bool CCoreAudioUnit::SetRenderProc()
213 {
214   if (!m_audioUnit || m_renderProc)
215     return false;
216
217   AURenderCallbackStruct callbackInfo;
218   callbackInfo.inputProc = RenderCallback; // Function to be called each time the AudioUnit needs data
219   callbackInfo.inputProcRefCon = this; // Pointer to be returned in the callback proc
220   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
221     kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
222   if (ret != noErr)
223   {
224     CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: "
225       "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   AudioUnitInitialize(m_audioUnit);
240
241   AURenderCallbackStruct callbackInfo;
242   callbackInfo.inputProc = nil;
243   callbackInfo.inputProcRefCon = nil;
244   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
245     kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
246   if (ret != noErr)
247   {
248     CLog::Log(LOGERROR, "CCoreAudioUnit::RemoveRenderProc: "
249       "Unable to remove AudioUnit render callback. Error = %s", GetError(ret).c_str());
250     return false;
251   }
252
253   m_renderProc = NULL;
254   Sleep(100);
255
256   return true;
257 }
258
259 OSStatus CCoreAudioUnit::RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
260   const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
261 {
262   OSStatus ret = noErr;
263   CCoreAudioUnit *audioUnit = (CCoreAudioUnit*)inRefCon;
264
265   if (audioUnit->m_pSource)
266   {
267     ret = audioUnit->m_pSource->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
268   }
269   else
270   {
271     ioData->mBuffers[0].mDataByteSize = 0;
272     if (ioActionFlags)
273       *ioActionFlags |=  kAudioUnitRenderAction_OutputIsSilence;
274   }
275
276
277   return ret;
278 }
279
280 void CCoreAudioUnit::GetFormatDesc(AEAudioFormat format,
281   AudioStreamBasicDescription *streamDesc, AudioStreamBasicDescription *coreaudioDesc)
282 {
283   unsigned int bps = CAEUtil::DataFormatToBits(format.m_dataFormat);
284
285   // Set the input stream format for the AudioUnit
286   // We use the default DefaultOuput AudioUnit, so we only can set the input stream format.
287   // The autput format is automaticaly set to the input format.
288   streamDesc->mFormatID = kAudioFormatLinearPCM;            //  Data encoding format
289   streamDesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
290   switch (format.m_dataFormat)
291   {
292     case AE_FMT_FLOAT:
293     case AE_FMT_LPCM:
294       streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
295       streamDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
296       break;
297     case AE_FMT_AC3:
298     case AE_FMT_DTS:
299     case AE_FMT_DTSHD:
300     case AE_FMT_TRUEHD:
301     case AE_FMT_EAC3:
302       streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
303       streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
304       break;
305     case AE_FMT_S16LE:
306       streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
307       break;
308     case AE_FMT_S16BE:
309       streamDesc->mFormatFlags |= kAudioFormatFlagIsBigEndian;
310       streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
311       break;
312     default:
313       streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
314       streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
315       break;
316   }
317   streamDesc->mChannelsPerFrame = format.m_channelLayout.Count();               // Number of interleaved audiochannels
318   streamDesc->mSampleRate = (Float64)format.m_sampleRate;                       //  the sample rate of the audio stream
319   streamDesc->mBitsPerChannel = bps;                                            // Number of bits per sample, per channel
320   streamDesc->mBytesPerFrame = (bps>>3) * format.m_channelLayout.Count();       // Size of a frame == 1 sample per channel
321   streamDesc->mFramesPerPacket = 1;                                             // The smallest amount of indivisible data. Always 1 for uncompressed audio
322   streamDesc->mBytesPerPacket = streamDesc->mBytesPerFrame * streamDesc->mFramesPerPacket;
323   streamDesc->mReserved = 0;
324
325   // Audio units use noninterleaved 32-bit floating point
326   //  linear PCM data for input and output, ...except in the 
327   // case of an audio unit that is a data format converter,
328   // which converts to or from this format.
329   coreaudioDesc->mFormatID = kAudioFormatLinearPCM;
330   coreaudioDesc->mFormatFlags = kAudioFormatFlagsNativeEndian |
331     kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
332   switch (format.m_dataFormat)
333   {
334     case AE_FMT_FLOAT:
335       coreaudioDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
336     default:
337       coreaudioDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
338       break;
339   }
340   coreaudioDesc->mBitsPerChannel   = bps; //sizeof(Float32)<<3;
341   coreaudioDesc->mSampleRate       = (Float64)format.m_sampleRate;;
342   coreaudioDesc->mFramesPerPacket  = 1;
343   coreaudioDesc->mChannelsPerFrame = streamDesc->mChannelsPerFrame;
344   coreaudioDesc->mBytesPerFrame    = (bps>>3); //sizeof(Float32);
345   coreaudioDesc->mBytesPerPacket   = (bps>>3); //sizeof(Float32);
346 }
347
348 float CCoreAudioUnit::GetLatency()
349 {
350   if (!m_audioUnit)
351     return 0.0f;
352
353   Float64 latency;
354   UInt32 size = sizeof(latency);
355
356   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
357     kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &latency, &size);
358
359   if (ret != noErr)
360   {
361     CLog::Log(LOGERROR, "CCoreAudioUnit::GetLatency: "
362       "Unable to set AudioUnit latency. Error = %s", GetError(ret).c_str());
363     return 0.0f;
364   }
365
366   return latency;
367 }
368
369 bool CCoreAudioUnit::Stop()
370 {
371   if (!m_audioUnit)
372     return false;
373
374   AudioOutputUnitStop(m_audioUnit);
375
376   return true;
377 }
378
379 bool CCoreAudioUnit::Start()
380 {
381   if (!m_audioUnit)
382     return false;
383
384   AudioOutputUnitStart(m_audioUnit);
385
386   return true;
387 }
388
389 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
390 // CAUOutputDevice
391 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
392 CAUOutputDevice::CAUOutputDevice() :
393   m_DeviceId    (NULL )
394 {
395 }
396
397 CAUOutputDevice::~CAUOutputDevice()
398 {
399 }
400
401 bool CAUOutputDevice::SetCurrentDevice(AudioDeviceID deviceId)
402 {
403   if (!m_audioUnit)
404     return false;
405
406   OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_CurrentDevice,
407     kAudioUnitScope_Global, kOutputBus, &deviceId, sizeof(AudioDeviceID));
408   if (ret != noErr)
409   {
410     CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentDevice: "
411       "Unable to set current device. Error = %s", GetError(ret).c_str());
412     return false;
413   }
414
415   m_DeviceId = deviceId;
416
417   return true;
418 }
419
420 bool CAUOutputDevice::GetChannelMap(CoreAudioChannelList* pChannelMap)
421 {
422   if (!m_audioUnit)
423     return false;
424
425   UInt32 size = 0;
426   Boolean writable = false;
427   AudioUnitGetPropertyInfo(m_audioUnit,
428     kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, &size, &writable);
429
430   UInt32 channels = size/sizeof(SInt32);
431   SInt32* pMap = new SInt32[channels];
432   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
433     kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, pMap, &size);
434   if (ret != noErr)
435     CLog::Log(LOGERROR, "CCoreAudioUnit::GetInputChannelMap: "
436       "Unable to retrieve AudioUnit input channel map. Error = %s", GetError(ret).c_str());
437   else
438     for (UInt32 i = 0; i < channels; i++)
439       pChannelMap->push_back(pMap[i]);
440   delete[] pMap;
441   return (!ret);
442 }
443
444 bool CAUOutputDevice::SetChannelMap(CoreAudioChannelList* pChannelMap)
445 {
446   // The number of array elements must match the
447   // number of output channels provided by the device
448   if (!m_audioUnit || !pChannelMap)
449     return false;
450
451   UInt32 channels = pChannelMap->size();
452   UInt32 size = sizeof(SInt32) * channels;
453   SInt32* pMap = new SInt32[channels];
454   for (UInt32 i = 0; i < channels; i++)
455     pMap[i] = (*pChannelMap)[i];
456
457   OSStatus ret = AudioUnitSetProperty(m_audioUnit,
458     kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, pMap, size);
459   if (ret != noErr)
460     CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: "
461       "Unable to get current device's buffer size. Error = %s", GetError(ret).c_str());
462   delete[] pMap;
463   return (!ret);
464 }
465
466 Float32 CAUOutputDevice::GetCurrentVolume()
467 {
468   if (!m_audioUnit)
469     return 0.0f;
470
471   Float32 volPct = 0.0f;
472   OSStatus ret = AudioUnitGetParameter(m_audioUnit,
473     kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &volPct);
474   if (ret != noErr)
475   {
476     CLog::Log(LOGERROR, "CCoreAudioUnit::GetCurrentVolume: "
477       "Unable to get AudioUnit volume. Error = %s", GetError(ret).c_str());
478     return 0.0f;
479   }
480   return volPct;
481 }
482
483 bool CAUOutputDevice::SetCurrentVolume(Float32 vol)
484 {
485   if (!m_audioUnit)
486     return false;
487
488   OSStatus ret = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume,
489     kAudioUnitScope_Global, 0, vol, 0);
490   if (ret != noErr)
491   {
492     CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentVolume: "
493       "Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
494     return false;
495   }
496   return true;
497 }
498
499 UInt32 CAUOutputDevice::GetBufferFrameSize()
500 {
501   if (!m_audioUnit)
502     return 0;
503
504   UInt32 bufferSize = 0;
505   UInt32 size = sizeof(UInt32);
506
507   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
508     kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Input, 0, &bufferSize, &size);
509   if (ret != noErr)
510   {
511     CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: "
512       "Unable to get current device's buffer size. Error = %s", GetError(ret).c_str());
513     return 0;
514   }
515   return bufferSize;
516 }
517
518 bool CAUOutputDevice::EnableInputOuput()
519 {
520   if (!m_audioUnit)
521     return false;
522
523   UInt32 hasio;
524   UInt32 size=sizeof(UInt32);
525   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
526     kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
527
528   if (hasio)
529   {
530     UInt32 enable;
531     enable = 0;
532     ret =  AudioUnitSetProperty(m_audioUnit,
533       kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enable, sizeof(enable));
534     if (ret != noErr)
535     {
536       CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: "
537         "Unable to enable input on bus 1. Error = %s", GetError(ret).c_str());
538       return false;
539     }
540
541     enable = 1;
542     ret = AudioUnitSetProperty(m_audioUnit,
543       kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &enable, sizeof(enable));
544     if (ret != noErr)
545     {
546       CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: "
547         "Unable to disable output on bus 0. Error = %s", GetError(ret).c_str());
548       return false;
549     }
550   }
551
552   return true;
553 }
554
555 bool CAUOutputDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
556 {
557   if (!m_DeviceId)
558     return false;
559
560   AudioObjectPropertyAddress propertyAddress; 
561   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput; 
562   propertyAddress.mElement  = 0;
563   propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; 
564   if (!AudioObjectHasProperty(m_DeviceId, &propertyAddress)) 
565     return false;
566
567   UInt32 propertySize = 0;
568   OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize); 
569   if (ret != noErr)
570     CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: "
571       "Unable to retrieve preferred channel layout size. Error = %s", GetError(ret).c_str());
572
573   void *pBuf = malloc(propertySize);
574   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf); 
575   if (ret != noErr)
576     CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: "
577       "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
578   else
579   {
580     // Copy the result into the caller's instance
581     layout.CopyLayout(*((AudioChannelLayout*)pBuf));
582   }
583   free(pBuf);
584   return (ret == noErr);
585 }
586
587 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
588 // CAUMatrixMixer
589 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
590 CAUMatrixMixer::CAUMatrixMixer()
591 {
592 }
593
594 CAUMatrixMixer::~CAUMatrixMixer()
595 {
596 }
597
598 bool CAUMatrixMixer::InitMatrixMixerVolumes()
599 {
600   // Fetch thechannel configuration
601   UInt32 dims[2];
602   UInt32 size = sizeof(dims);
603   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
604     kAudioUnitProperty_MatrixDimensions, kAudioUnitScope_Global, 0, dims, &size);
605   if (ret)
606   {
607     CLog::Log(LOGERROR, "CAUMatrixMixer::Initialize:: "
608       "Get matrix dimesion. Error = %s", GetError(ret).c_str());
609     return false;
610   }
611
612   // Initialize global, input, and output levels
613   if (!SetGlobalVolume(1.0f))
614     return false;
615   for (UInt32 i = 0; i < dims[0]; i++)
616     if (!SetInputVolume(i, 1.0f))
617       return false;
618   for (UInt32 i = 0; i < dims[1]; i++)
619     if (!SetOutputVolume(i, 1.0f))
620       return false;
621
622   return true;
623 }
624
625 UInt32 CAUMatrixMixer::GetInputBusCount()
626 {
627   if (!m_audioUnit)
628     return 0;
629
630   UInt32 busCount = 0;
631   UInt32 size = sizeof(busCount);
632   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
633     kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, &size);
634   if (ret)
635   {
636     CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputBusCount: "
637       "Unable to get input bus count. Error = %s", GetError(ret).c_str());
638     return 0;
639   }
640   return busCount;
641 }
642
643 bool CAUMatrixMixer::SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat)
644 {
645   if (!m_audioUnit)
646     return false;
647
648   UInt32 enable = 1;
649   for (UInt32 i = 0; i < busCount; i++)
650   {
651     AudioUnitSetParameter(m_audioUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Input, i, enable, 0);
652     if (!SetFormat(pFormat, kAudioUnitScope_Input, i))
653       return false;
654   }
655
656   return true;
657 }
658
659 bool CAUMatrixMixer::SetInputBusCount(UInt32 busCount)
660 {
661   if (!m_audioUnit)
662     return false;
663
664   OSStatus ret = AudioUnitSetProperty(m_audioUnit,
665     kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(UInt32));
666   if (ret)
667   {
668     CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputBusCount: "
669       "Unable to set input bus count. Error = %s", GetError(ret).c_str());
670     return false;
671   }
672   return true;
673 }
674
675 UInt32 CAUMatrixMixer::GetOutputBusCount()
676 {
677   if (!m_audioUnit)
678     return 0;
679
680   UInt32 busCount = 0;
681   UInt32 size = sizeof(busCount);
682   OSStatus ret = AudioUnitGetProperty(m_audioUnit,
683     kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, &size);
684   if (ret)
685   {
686     CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputBusCount: "
687       "Unable to get output bus count. Error = %s", GetError(ret).c_str());
688     return 0;
689   }
690   return busCount;
691 }
692
693 bool CAUMatrixMixer::SetOutputBusCount(UInt32 busCount)
694 {
695   if (!m_audioUnit)
696     return false;
697
698   OSStatus ret = AudioUnitSetProperty(m_audioUnit,
699     kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &busCount, sizeof(UInt32));
700   if (ret)
701   {
702     CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputBusCount: "
703       "Unable to set output bus count. Error = %s", GetError(ret).c_str());
704     return false;
705   }
706   return true;
707 }
708
709 Float32 CAUMatrixMixer::GetGlobalVolume()
710 {
711   if (!m_audioUnit)
712     return 0.0f;
713
714   Float32 vol = 0.0f;
715   OSStatus ret = AudioUnitGetParameter(m_audioUnit,
716     kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, &vol);
717   if (ret)
718   {
719     CLog::Log(LOGERROR, "CAUMatrixMixer::GetGlobalVolume: "
720       "Unable to get global volume. Error = %s", GetError(ret).c_str());
721     return 0.0f;
722   }
723   return vol;
724 }
725
726 bool CAUMatrixMixer::SetGlobalVolume(Float32 vol)
727 {
728   if (!m_audioUnit)
729     return false;
730
731   OSStatus ret = AudioUnitSetParameter(m_audioUnit,
732     kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, vol, 0);
733   if (ret)
734   {
735     CLog::Log(LOGERROR, "CAUMatrixMixer::SetGlobalVolume: "
736       "Unable to set global volume. Error = %s", GetError(ret).c_str());
737     return false;
738   }
739   return true;
740 }
741
742 Float32 CAUMatrixMixer::GetInputVolume(UInt32 element)
743 {
744   if (!m_audioUnit)
745     return 0.0f;
746
747   Float32 vol = 0.0f;
748   OSStatus ret = AudioUnitGetParameter(m_audioUnit,
749     kMatrixMixerParam_Volume, kAudioUnitScope_Input, element, &vol);
750   if (ret)
751   {
752     CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputVolume: "
753       "Unable to get input volume. Error = %s", GetError(ret).c_str());
754     return 0.0f;
755   }
756   return vol;
757 }
758
759 bool CAUMatrixMixer::SetInputVolume(UInt32 element, Float32 vol)
760 {
761   if (!m_audioUnit)
762     return false;
763
764   OSStatus ret = AudioUnitSetParameter(m_audioUnit,
765     kMatrixMixerParam_Volume, kAudioUnitScope_Input, element, vol, 0);
766   if (ret)
767   {
768     CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputVolume: "
769       "Unable to set input volume. Error = %s", GetError(ret).c_str());
770     return false;
771   }
772   return true;
773 }
774
775 Float32 CAUMatrixMixer::GetOutputVolume(UInt32 element)
776 {
777   if (!m_audioUnit)
778     return 0.0f;
779
780   Float32 vol = 0.0f;
781   OSStatus ret = AudioUnitGetParameter(m_audioUnit,
782     kMatrixMixerParam_Volume, kAudioUnitScope_Output, element, &vol);
783   if (ret)
784   {
785     CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputVolume: "
786       "Unable to get output volume. Error = %s", GetError(ret).c_str());
787     return 0.0f;
788   }
789   return vol;
790 }
791
792 bool CAUMatrixMixer::SetOutputVolume(UInt32 element, Float32 vol)
793 {
794   if (!m_audioUnit)
795     return false;
796
797   OSStatus ret = AudioUnitSetParameter(m_audioUnit,
798     kMatrixMixerParam_Volume, kAudioUnitScope_Output, element, vol, 0);
799   if (ret)
800   {
801     CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputVolume: "
802       "Unable to set output volume. Error = %s", GetError(ret).c_str());
803     return false;
804   }
805   return true;
806 }
807