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