[release] version bump to 13.0 beta1
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / CoreAudioDevice.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 "CoreAudioDevice.h"
22 #include "CoreAudioAEHAL.h"
23 #include "CoreAudioChannelLayout.h"
24 #include "CoreAudioHardware.h"
25 #include "utils/log.h"
26
27 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
28 // CCoreAudioDevice
29 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
30 CCoreAudioDevice::CCoreAudioDevice()  :
31   m_Started             (false    ),
32   m_pSource             (NULL     ),
33   m_DeviceId            (0        ),
34   m_MixerRestore        (-1       ),
35   m_IoProc              (NULL     ),
36   m_ObjectListenerProc  (NULL     ),
37   m_SampleRateRestore   (0.0f     ),
38   m_HogPid              (-1       ),
39   m_frameSize           (0        ),
40   m_OutputBufferIndex   (0        ),
41   m_BufferSizeRestore   (0        )
42 {
43 }
44
45 CCoreAudioDevice::CCoreAudioDevice(AudioDeviceID deviceId) :
46   m_Started             (false    ),
47   m_pSource             (NULL     ),
48   m_DeviceId            (deviceId ),
49   m_MixerRestore        (-1       ),
50   m_IoProc              (NULL     ),
51   m_ObjectListenerProc  (NULL     ),
52   m_SampleRateRestore   (0.0f     ),
53   m_HogPid              (-1       ),
54   m_frameSize           (0        ),
55   m_OutputBufferIndex   (0        ),
56   m_BufferSizeRestore   (0        )
57 {
58 }
59
60 CCoreAudioDevice::~CCoreAudioDevice()
61 {
62   Close();
63 }
64
65 bool CCoreAudioDevice::Open(AudioDeviceID deviceId)
66 {
67   m_DeviceId = deviceId;
68   m_BufferSizeRestore = GetBufferSize();
69   return true;
70 }
71
72 void CCoreAudioDevice::Close()
73 {
74   if (!m_DeviceId)
75     return;
76
77   // Stop the device if it was started
78   Stop();
79
80   // Unregister the IOProc if we have one
81   if (m_IoProc)
82     SetInputSource(NULL, 0, 0);
83
84   SetHogStatus(false);
85   CCoreAudioHardware::SetAutoHogMode(false);
86
87   if (m_MixerRestore > -1) // We changed the mixer status
88     SetMixingSupport((m_MixerRestore ? true : false));
89   m_MixerRestore = -1;
90
91   if (m_SampleRateRestore != 0.0f)
92     SetNominalSampleRate(m_SampleRateRestore);
93
94   if (m_BufferSizeRestore && m_BufferSizeRestore != GetBufferSize())
95   {
96     SetBufferSize(m_BufferSizeRestore);
97     m_BufferSizeRestore = 0;
98   }
99
100   m_IoProc = NULL;
101   m_pSource = NULL;
102   m_DeviceId = 0;
103   m_ObjectListenerProc = NULL;
104 }
105
106 void CCoreAudioDevice::Start()
107 {
108   if (!m_DeviceId || m_Started)
109     return;
110
111   OSStatus ret = AudioDeviceStart(m_DeviceId, m_IoProc);
112   if (ret)
113     CLog::Log(LOGERROR, "CCoreAudioDevice::Start: "
114       "Unable to start device. Error = %s", GetError(ret).c_str());
115   else
116     m_Started = true;
117 }
118
119 void CCoreAudioDevice::Stop()
120 {
121   if (!m_DeviceId || !m_Started)
122     return;
123
124   OSStatus ret = AudioDeviceStop(m_DeviceId, m_IoProc);
125   if (ret)
126     CLog::Log(LOGERROR, "CCoreAudioDevice::Stop: "
127       "Unable to stop device. Error = %s", GetError(ret).c_str());
128   m_Started = false;
129 }
130
131 void CCoreAudioDevice::RemoveObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
132 {
133   if (!m_DeviceId)
134     return;
135
136   AudioObjectPropertyAddress audioProperty;
137   audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
138   audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
139   audioProperty.mElement = kAudioObjectPropertyElementWildcard;
140
141   OSStatus ret = AudioObjectRemovePropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
142   if (ret)
143   {
144     CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveObjectListenerProc: "
145       "Unable to set ObjectListener callback. Error = %s", GetError(ret).c_str());
146   }
147   m_ObjectListenerProc = NULL;
148 }
149
150 bool CCoreAudioDevice::SetObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
151 {
152   // Allow only one ObjectListener at a time
153   if (!m_DeviceId || m_ObjectListenerProc)
154     return false;
155
156   AudioObjectPropertyAddress audioProperty;
157   audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
158   audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
159   audioProperty.mElement = kAudioObjectPropertyElementWildcard;
160
161   OSStatus ret = AudioObjectAddPropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
162
163   if (ret)
164   {
165     CLog::Log(LOGERROR, "CCoreAudioDevice::SetObjectListenerProc: "
166       "Unable to remove ObjectListener callback. Error = %s", GetError(ret).c_str());
167     return false;
168   }
169
170   m_ObjectListenerProc = callback;
171   return true;
172 }
173
174 bool CCoreAudioDevice::SetInputSource(ICoreAudioSource* pSource, unsigned int frameSize, unsigned int outputBufferIndex)
175 {
176   m_pSource   = pSource;
177   m_frameSize = frameSize;
178   m_OutputBufferIndex = outputBufferIndex;
179
180   if (pSource)
181     return AddIOProc();
182   else
183     return RemoveIOProc();
184 }
185
186 bool CCoreAudioDevice::AddIOProc()
187 {
188   // Allow only one IOProc at a time
189   if (!m_DeviceId || m_IoProc)
190     return false;
191
192   OSStatus ret = AudioDeviceCreateIOProcID(m_DeviceId, DirectRenderCallback, this, &m_IoProc);
193   if (ret)
194   {
195     CLog::Log(LOGERROR, "CCoreAudioDevice::AddIOProc: "
196       "Unable to add IOProc. Error = %s", GetError(ret).c_str());
197     m_IoProc = NULL;
198     return false;
199   }
200
201   Start();
202
203   return true;
204 }
205
206 bool CCoreAudioDevice::RemoveIOProc()
207 {
208   if (!m_DeviceId || !m_IoProc)
209     return false;
210
211   Stop();
212
213   OSStatus ret = AudioDeviceDestroyIOProcID(m_DeviceId, m_IoProc);
214   if (ret)
215     CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveIOProc: "
216       "Unable to remove IOProc. Error = %s", GetError(ret).c_str());
217
218   m_IoProc = NULL; // Clear the reference no matter what
219   m_pSource = NULL;
220
221   Sleep(100);
222
223   return true;
224 }
225
226 std::string CCoreAudioDevice::GetName()
227 {
228   if (!m_DeviceId)
229     return NULL;
230
231   AudioObjectPropertyAddress  propertyAddress;
232   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
233   propertyAddress.mElement  = 0;
234   propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
235
236   UInt32 propertySize;
237   OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
238   if (ret != noErr)
239     return NULL;
240
241   std::string name = "";
242   char *buff = new char[propertySize + 1];
243   buff[propertySize] = 0x00;
244   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, buff);
245   if (ret != noErr)
246   {
247     CLog::Log(LOGERROR, "CCoreAudioDevice::GetName: "
248       "Unable to get device name - id: 0x%04x. Error = %s", (uint)m_DeviceId, GetError(ret).c_str());
249   }
250   else
251   {
252     name = buff;
253   }
254   delete[] buff;
255
256
257   return name;
258 }
259
260 UInt32 CCoreAudioDevice::GetTotalOutputChannels()
261 {
262   UInt32 channels = 0;
263
264   if (!m_DeviceId)
265     return channels;
266
267   AudioObjectPropertyAddress  propertyAddress;
268   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
269   propertyAddress.mElement  = 0;
270   propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
271
272   UInt32 size = 0;
273   OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &size);
274   if (ret != noErr)
275     return channels;
276
277   AudioBufferList* pList = (AudioBufferList*)malloc(size);
278   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, pList);
279   if (ret == noErr)
280   {
281     for(UInt32 buffer = 0; buffer < pList->mNumberBuffers; ++buffer)
282       channels += pList->mBuffers[buffer].mNumberChannels;
283   }
284   else
285   {
286     CLog::Log(LOGERROR, "CCoreAudioDevice::GetTotalOutputChannels: "
287       "Unable to get total device output channels - id: 0x%04x. Error = %s",
288       (uint)m_DeviceId, GetError(ret).c_str());
289   }
290
291   free(pList);
292
293   return channels;
294 }
295
296 bool CCoreAudioDevice::GetStreams(AudioStreamIdList* pList)
297 {
298   if (!pList || !m_DeviceId)
299     return false;
300
301   AudioObjectPropertyAddress  propertyAddress;
302   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
303   propertyAddress.mElement  = 0;
304   propertyAddress.mSelector = kAudioDevicePropertyStreams;
305
306   UInt32  propertySize = 0;
307   OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
308   if (ret != noErr)
309     return false;
310
311   UInt32 streamCount = propertySize / sizeof(AudioStreamID);
312   AudioStreamID* pStreamList = new AudioStreamID[streamCount];
313   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pStreamList);
314   if (ret == noErr)
315   {
316     for (UInt32 stream = 0; stream < streamCount; stream++)
317       pList->push_back(pStreamList[stream]);
318   }
319   delete[] pStreamList;
320
321   return ret == noErr;
322 }
323
324
325 bool CCoreAudioDevice::IsRunning()
326 {
327   AudioObjectPropertyAddress  propertyAddress;
328   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
329   propertyAddress.mElement  = 0;
330   propertyAddress.mSelector = kAudioDevicePropertyDeviceIsRunning;
331
332   UInt32 isRunning = 0;
333   UInt32 propertySize = sizeof(isRunning);
334   OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &isRunning);
335   if (ret != noErr)
336     return false;
337
338   return isRunning != 0;
339 }
340
341 bool CCoreAudioDevice::SetHogStatus(bool hog)
342 {
343   // According to Jeff Moore (Core Audio, Apple), Setting kAudioDevicePropertyHogMode
344   // is a toggle and the only way to tell if you do get hog mode is to compare
345   // the returned pid against getpid, if the match, you have hog mode, if not you don't.
346   if (!m_DeviceId)
347     return false;
348
349   AudioObjectPropertyAddress  propertyAddress;
350   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
351   propertyAddress.mElement  = 0;
352   propertyAddress.mSelector = kAudioDevicePropertyHogMode;
353
354   if (hog)
355   {
356     // Not already set
357     if (m_HogPid == -1)
358     {
359       OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(m_HogPid), &m_HogPid);
360
361       // even if setting hogmode was successfull our PID might not get written
362       // into m_HogPid (so it stays -1). Readback hogstatus for judging if we
363       // had success on getting hog status
364       // We do this only when AudioObjectSetPropertyData didn't set m_HogPid because
365       // it seems that in the other cases the GetHogStatus could return -1
366       // which would overwrite our valid m_HogPid again
367       // Man we should never touch this shit again ;)
368       if (m_HogPid == -1)
369         m_HogPid = GetHogStatus();
370
371       if (ret || m_HogPid != getpid())
372       {
373         CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
374           "Unable to set 'hog' status. Error = %s", GetError(ret).c_str());
375         return false;
376       }
377     }
378   }
379   else
380   {
381     // Currently Set
382     if (m_HogPid > -1)
383     {
384       pid_t hogPid = -1;
385       OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(hogPid), &hogPid);
386       if (ret || hogPid == getpid())
387       {
388         CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
389           "Unable to release 'hog' status. Error = %s", GetError(ret).c_str());
390         return false;
391       }
392       // Reset internal state
393       m_HogPid = hogPid;
394     }
395   }
396   return true;
397 }
398
399 pid_t CCoreAudioDevice::GetHogStatus()
400 {
401   if (!m_DeviceId)
402     return false;
403
404   AudioObjectPropertyAddress  propertyAddress;
405   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
406   propertyAddress.mElement  = 0;
407   propertyAddress.mSelector = kAudioDevicePropertyHogMode;
408
409   pid_t hogPid = -1;
410   UInt32 size = sizeof(hogPid);
411   AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &hogPid);
412
413   return hogPid;
414 }
415
416 bool CCoreAudioDevice::SetMixingSupport(UInt32 mix)
417 {
418   if (!m_DeviceId)
419     return false;
420
421   if (!GetMixingSupport())
422     return false;
423
424   int restore = -1;
425   if (m_MixerRestore == -1)
426   {
427     // This is our first change to this setting. Store the original setting for restore
428     restore = (GetMixingSupport() ? 1 : 0);
429   }
430
431   AudioObjectPropertyAddress  propertyAddress;
432   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
433   propertyAddress.mElement  = 0;
434   propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
435
436   UInt32 mixEnable = mix ? 1 : 0;
437   OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(mixEnable), &mixEnable);
438   if (ret != noErr)
439   {
440     CLog::Log(LOGERROR, "CCoreAudioDevice::SetMixingSupport: "
441       "Unable to set MixingSupport to %s. Error = %s", mix ? "'On'" : "'Off'", GetError(ret).c_str());
442     return false;
443   }
444   if (m_MixerRestore == -1)
445     m_MixerRestore = restore;
446   return true;
447 }
448
449 bool CCoreAudioDevice::GetMixingSupport()
450 {
451   if (!m_DeviceId)
452     return false;
453
454   UInt32    size;
455   UInt32    mix = 0;
456   Boolean   writable = false;
457
458   AudioObjectPropertyAddress  propertyAddress;
459   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
460   propertyAddress.mElement  = 0;
461   propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
462
463   if( AudioObjectHasProperty( m_DeviceId, &propertyAddress ) )
464   {
465     OSStatus ret = AudioObjectIsPropertySettable(m_DeviceId, &propertyAddress, &writable);
466     if (ret)
467     {
468       CLog::Log(LOGERROR, "CCoreAudioDevice::SupportsMixing: "
469         "Unable to get propertyinfo mixing support. Error = %s", GetError(ret).c_str());
470       writable = false;
471     }
472
473     if (writable)
474     {
475       size = sizeof(mix);
476       ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &mix);
477       if (ret != noErr)
478         mix = 0;
479     }
480   }
481   CLog::Log(LOGERROR, "CCoreAudioDevice::SupportsMixing: "
482     "Device mixing support : %s.", mix ? "'Yes'" : "'No'");
483
484   return (mix > 0);
485 }
486
487 bool CCoreAudioDevice::SetCurrentVolume(Float32 vol)
488 {
489   if (!m_DeviceId)
490     return false;
491
492   AudioObjectPropertyAddress  propertyAddress;
493   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
494   propertyAddress.mElement  = 0;
495   propertyAddress.mSelector = kHALOutputParam_Volume;
496
497   OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float32), &vol);
498   if (ret != noErr)
499   {
500     CLog::Log(LOGERROR, "CCoreAudioDevice::SetCurrentVolume: "
501       "Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
502     return false;
503   }
504   return true;
505 }
506
507 bool CCoreAudioDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
508 {
509   if (!m_DeviceId)
510     return false;
511
512   AudioObjectPropertyAddress  propertyAddress;
513   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
514   propertyAddress.mElement  = 0;
515   propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
516
517   UInt32 propertySize = 0;
518   OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
519   if (ret)
520     return false;
521
522   void* pBuf = malloc(propertySize);
523   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf);
524   if (ret != noErr)
525     CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: "
526       "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
527   else
528   {
529     // Copy the result into the caller's instance
530     layout.CopyLayout(*((AudioChannelLayout*)pBuf));
531   }
532   free(pBuf);
533   return (ret == noErr);
534 }
535
536 bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList)
537 {
538   if (!pList || !m_DeviceId)
539     return false;
540
541   AudioObjectPropertyAddress  propertyAddress;
542   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
543   propertyAddress.mElement  = 0;
544   propertyAddress.mSelector = kAudioDevicePropertyDataSources;
545
546   UInt32 propertySize = 0;
547   OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
548   if (ret != noErr)
549     return false;
550
551   UInt32  sources = propertySize / sizeof(UInt32);
552   UInt32* pSources = new UInt32[sources];
553   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pSources);
554   if (ret == noErr)
555   {
556     for (UInt32 i = 0; i < sources; i++)
557       pList->push_back(pSources[i]);
558   }
559   delete[] pSources;
560   return (!ret);
561 }
562
563 Float64 CCoreAudioDevice::GetNominalSampleRate()
564 {
565   if (!m_DeviceId)
566     return 0.0f;
567
568   AudioObjectPropertyAddress  propertyAddress;
569   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
570   propertyAddress.mElement  = 0;
571   propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
572
573   Float64 sampleRate = 0.0f;
574   UInt32  propertySize = sizeof(Float64);
575   OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &sampleRate);
576   if (ret != noErr)
577   {
578     CLog::Log(LOGERROR, "CCoreAudioDevice::GetNominalSampleRate: "
579       "Unable to retrieve current device sample rate. Error = %s", GetError(ret).c_str());
580     return 0.0f;
581   }
582   return sampleRate;
583 }
584
585 bool CCoreAudioDevice::SetNominalSampleRate(Float64 sampleRate)
586 {
587   if (!m_DeviceId || sampleRate == 0.0f)
588     return false;
589
590   Float64 currentRate = GetNominalSampleRate();
591   if (currentRate == sampleRate)
592     return true; //No need to change
593
594   AudioObjectPropertyAddress  propertyAddress;
595   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
596   propertyAddress.mElement  = 0;
597   propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
598
599   OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float64), &sampleRate);
600   if (ret != noErr)
601   {
602     CLog::Log(LOGERROR, "CCoreAudioDevice::SetNominalSampleRate: "
603       "Unable to set current device sample rate to %0.0f. Error = %s",
604       (float)sampleRate, GetError(ret).c_str());
605     return false;
606   }
607   if (m_SampleRateRestore == 0.0f)
608     m_SampleRateRestore = currentRate;
609
610   return true;
611 }
612
613 UInt32 CCoreAudioDevice::GetNumLatencyFrames()
614 {
615   UInt32 num_latency_frames = 0;
616   if (!m_DeviceId)
617     return 0;
618
619   // number of frames of latency in the AudioDevice
620   AudioObjectPropertyAddress  propertyAddress;
621   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
622   propertyAddress.mElement  = 0;
623   propertyAddress.mSelector = kAudioDevicePropertyLatency;
624
625   UInt32 i_param = 0;
626   UInt32 i_param_size = sizeof(uint32_t);
627   OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
628   if (ret == noErr)
629     num_latency_frames += i_param;
630
631   // number of frames in the IO buffers
632   propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
633   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
634   if (ret == noErr)
635     num_latency_frames += i_param;
636
637   // number for frames in ahead the current hardware position that is safe to do IO
638   propertyAddress.mSelector = kAudioDevicePropertySafetyOffset;
639   ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
640   if (ret == noErr)
641     num_latency_frames += i_param;
642
643   return (num_latency_frames);
644 }
645
646 UInt32 CCoreAudioDevice::GetBufferSize()
647 {
648   if (!m_DeviceId)
649     return false;
650
651   AudioObjectPropertyAddress  propertyAddress;
652   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
653   propertyAddress.mElement  = 0;
654   propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
655
656   UInt32 size = 0;
657   UInt32 propertySize = sizeof(size);
658   OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &size);
659   if (ret != noErr)
660     CLog::Log(LOGERROR, "CCoreAudioDevice::GetBufferSize: "
661       "Unable to retrieve buffer size. Error = %s", GetError(ret).c_str());
662   return size;
663 }
664
665 bool CCoreAudioDevice::SetBufferSize(UInt32 size)
666 {
667   if (!m_DeviceId)
668     return false;
669
670   AudioObjectPropertyAddress  propertyAddress;
671   propertyAddress.mScope    = kAudioDevicePropertyScopeOutput;
672   propertyAddress.mElement  = 0;
673   propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
674
675   UInt32 propertySize = sizeof(size);
676   OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, propertySize, &size);
677   if (ret != noErr)
678   {
679     CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: "
680       "Unable to set buffer size. Error = %s", GetError(ret).c_str());
681   }
682
683   if (GetBufferSize() != size)
684     CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: Buffer size change not applied.");
685
686   return (ret == noErr);
687 }
688
689 OSStatus CCoreAudioDevice::DirectRenderCallback(AudioDeviceID inDevice,
690   const AudioTimeStamp  *inNow,
691   const AudioBufferList *inInputData,
692   const AudioTimeStamp  *inInputTime,
693   AudioBufferList       *outOutputData,
694   const AudioTimeStamp  *inOutputTime,
695   void                  *inClientData)
696 {
697   OSStatus ret = noErr;
698   CCoreAudioDevice *audioDevice = (CCoreAudioDevice*)inClientData;
699
700   if (audioDevice->m_pSource && audioDevice->m_frameSize)
701   {
702     UInt32 frames = outOutputData->mBuffers[audioDevice->m_OutputBufferIndex].mDataByteSize / audioDevice->m_frameSize;
703     ret = audioDevice->m_pSource->Render(NULL, inInputTime, 0, frames, outOutputData);
704   }
705   else
706   {
707     outOutputData->mBuffers[audioDevice->m_OutputBufferIndex].mDataByteSize = 0;
708   }
709
710   return ret;
711 }