Merge pull request #4875 from koying/fixdroidremotekeyboard
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / osx / CoreAudioStream.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 "CoreAudioStream.h"
22
23 #include "CoreAudioHelpers.h"
24 #include "utils/log.h"
25 #include "utils/StdString.h"
26
27 CCoreAudioStream::CCoreAudioStream() :
28   m_StreamId  (0    )
29 {
30   m_OriginalVirtualFormat.mFormatID = 0;
31   m_OriginalPhysicalFormat.mFormatID = 0;
32 }
33
34 CCoreAudioStream::~CCoreAudioStream()
35 {
36   Close();
37 }
38
39 bool CCoreAudioStream::Open(AudioStreamID streamId)
40 {
41   m_StreamId = streamId;
42   CLog::Log(LOGDEBUG, "CCoreAudioStream::Open: Opened stream 0x%04x.", (uint)m_StreamId);
43
44   // watch for physical property changes.
45   AudioObjectPropertyAddress propertyAOPA;
46   propertyAOPA.mScope    = kAudioObjectPropertyScopeGlobal;
47   propertyAOPA.mElement  = kAudioObjectPropertyElementMaster;  
48   propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
49   if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
50     CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a physical property listener.");
51
52   // watch for virtual property changes.
53   propertyAOPA.mScope    = kAudioObjectPropertyScopeGlobal;
54   propertyAOPA.mElement  = kAudioObjectPropertyElementMaster;  
55   propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
56   if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
57     CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a virtual property listener.");
58
59   return true;
60 }
61
62 // TODO: Should it even be possible to change both the 
63 // physical and virtual formats, since the devices do it themselves?
64 void CCoreAudioStream::Close(bool restore)
65 {
66   if (!m_StreamId)
67     return;
68
69   std::string formatString;
70
71   // remove the physical/virtual property listeners before we make changes
72   // that will trigger callbacks that we do not care about.
73   AudioObjectPropertyAddress propertyAOPA;
74   propertyAOPA.mScope    = kAudioObjectPropertyScopeGlobal;
75   propertyAOPA.mElement  = kAudioObjectPropertyElementMaster;  
76   propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
77   if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
78     CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
79
80   propertyAOPA.mScope    = kAudioObjectPropertyScopeGlobal;
81   propertyAOPA.mElement  = kAudioObjectPropertyElementMaster;  
82   propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
83   if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
84     CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
85
86   // Revert any format changes we made
87   if (restore && m_OriginalVirtualFormat.mFormatID && m_StreamId)
88   {
89     CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
90       "Restoring original virtual format for stream 0x%04x. (%s)",
91       (uint)m_StreamId, StreamDescriptionToString(m_OriginalVirtualFormat, formatString));
92     AudioStreamBasicDescription setFormat = m_OriginalVirtualFormat;
93     SetVirtualFormat(&setFormat);
94   }
95   if (restore && m_OriginalPhysicalFormat.mFormatID && m_StreamId)
96   {
97     CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
98       "Restoring original physical format for stream 0x%04x. (%s)",
99       (uint)m_StreamId, StreamDescriptionToString(m_OriginalPhysicalFormat, formatString));
100     AudioStreamBasicDescription setFormat = m_OriginalPhysicalFormat;
101     SetPhysicalFormat(&setFormat);
102   }
103
104   m_OriginalVirtualFormat.mFormatID  = 0;
105   m_OriginalPhysicalFormat.mFormatID = 0;
106   CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Closed stream 0x%04x.", (uint)m_StreamId);
107   m_StreamId = 0;
108 }
109
110 UInt32 CCoreAudioStream::GetDirection()
111 {
112   if (!m_StreamId)
113     return 0;
114
115   UInt32 val = 0;
116   UInt32 size = sizeof(UInt32);
117
118   AudioObjectPropertyAddress propertyAddress; 
119   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
120   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
121   propertyAddress.mSelector = kAudioStreamPropertyDirection; 
122
123   OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, &val); 
124   if (ret)
125     return 0;
126
127   return val;
128 }
129
130 // WARNING - don't rely on this method - the return value of
131 // GetTerminalType is driver specific - the checked return
132 // values are only recommendations from apple
133 bool CCoreAudioStream::IsDigitalOuptut(AudioStreamID id)
134 {
135   UInt32 type = GetTerminalType(id);
136   // yes apple is mixing types here...
137   return (type == kAudioStreamTerminalTypeDigitalAudioInterface ||
138           type == kIOAudioDeviceTransportTypeDisplayPort ||
139           type == kIOAudioDeviceTransportTypeHdmi ||
140           type == kIOAudioDeviceTransportTypeFireWire ||
141           type == kIOAudioDeviceTransportTypeThunderbolt ||
142           type == kIOAudioDeviceTransportTypeUSB);
143 }
144
145 UInt32 CCoreAudioStream::GetTerminalType(AudioStreamID id)
146 {
147   if (!id)
148     return 0;
149
150   UInt32 val = 0;
151   UInt32 size = sizeof(UInt32);
152
153   AudioObjectPropertyAddress propertyAddress; 
154   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
155   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
156   propertyAddress.mSelector = kAudioStreamPropertyTerminalType; 
157
158   OSStatus ret = AudioObjectGetPropertyData(id, &propertyAddress, 0, NULL, &size, &val);
159   if (ret)
160     return 0;
161   return val;
162 }
163
164 UInt32 CCoreAudioStream::GetNumLatencyFrames()
165 {
166   if (!m_StreamId)
167     return 0;
168
169   UInt32 i_param_size = sizeof(uint32_t);
170   UInt32 i_param, num_latency_frames = 0;
171
172   AudioObjectPropertyAddress propertyAddress; 
173   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
174   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
175   propertyAddress.mSelector = kAudioStreamPropertyLatency; 
176
177   // number of frames of latency in the AudioStream
178   OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &i_param_size, &i_param); 
179   if (ret == noErr)
180   {
181     num_latency_frames += i_param;
182   }
183
184   return num_latency_frames;
185 }
186
187 bool CCoreAudioStream::GetVirtualFormat(AudioStreamBasicDescription* pDesc)
188 {
189   if (!pDesc || !m_StreamId)
190     return false;
191
192   UInt32 size = sizeof(AudioStreamBasicDescription);
193
194   AudioObjectPropertyAddress propertyAddress; 
195   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
196   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
197   propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat; 
198   OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &size); 
199   if (ret)
200     return false;
201
202   ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, pDesc); 
203   if (ret)
204     return false;
205   return true;
206 }
207
208 bool CCoreAudioStream::SetVirtualFormat(AudioStreamBasicDescription* pDesc)
209 {
210   if (!pDesc || !m_StreamId)
211     return false;
212
213   std::string formatString;
214
215   if (!m_OriginalVirtualFormat.mFormatID)
216   {
217     // Store the original format (as we found it) so that it can be restored later
218     if (!GetVirtualFormat(&m_OriginalVirtualFormat))
219     {
220       CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
221         "Unable to retrieve current virtual format for stream 0x%04x.", (uint)m_StreamId);
222       return false;
223     }
224   }
225   m_virtual_format_event.Reset();
226
227   AudioObjectPropertyAddress propertyAddress; 
228   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
229   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
230   propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat; 
231
232   UInt32 propertySize = sizeof(AudioStreamBasicDescription);
233   OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc); 
234   if (ret)
235   {
236     CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
237       "Unable to set virtual format for stream 0x%04x. Error = %s",
238       (uint)m_StreamId, GetError(ret).c_str());
239     return false;
240   }
241
242   // The AudioStreamSetProperty is not only asynchronious,
243   // it is also not Atomic, in its behaviour.
244   // Therefore we check 5 times before we really give up.
245   // FIXME: failing isn't actually implemented yet.
246   for (int i = 0; i < 10; ++i)
247   {
248     AudioStreamBasicDescription checkVirtualFormat;
249     if (!GetVirtualFormat(&checkVirtualFormat))
250     {
251       CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
252         "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
253       return false;
254     }
255     if (checkVirtualFormat.mSampleRate == pDesc->mSampleRate &&
256         checkVirtualFormat.mFormatID == pDesc->mFormatID &&
257         checkVirtualFormat.mFramesPerPacket == pDesc->mFramesPerPacket)
258     {
259       // The right format is now active.
260       CLog::Log(LOGDEBUG, "CCoreAudioStream::SetVirtualFormat: "
261         "Virtual format for stream 0x%04x. now active (%s)",
262         (uint)m_StreamId, StreamDescriptionToString(checkVirtualFormat, formatString));
263       break;
264     }
265     m_virtual_format_event.WaitMSec(100);
266   }
267   return true;
268 }
269
270 bool CCoreAudioStream::GetPhysicalFormat(AudioStreamBasicDescription* pDesc)
271 {
272   if (!pDesc || !m_StreamId)
273     return false;
274
275   UInt32 size = sizeof(AudioStreamBasicDescription);
276
277   AudioObjectPropertyAddress propertyAddress; 
278   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
279   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
280   propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat; 
281
282   OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, pDesc); 
283   if (ret)
284     return false;
285   return true;
286 }
287
288 bool CCoreAudioStream::SetPhysicalFormat(AudioStreamBasicDescription* pDesc)
289 {
290   if (!pDesc || !m_StreamId)
291     return false;
292
293   std::string formatString;
294
295   if (!m_OriginalPhysicalFormat.mFormatID)
296   {
297     // Store the original format (as we found it) so that it can be restored later
298     if (!GetPhysicalFormat(&m_OriginalPhysicalFormat))
299     {
300       CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
301         "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
302       return false;
303     }
304   }
305   m_physical_format_event.Reset();
306
307   AudioObjectPropertyAddress propertyAddress; 
308   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
309   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
310   propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat; 
311
312   UInt32 propertySize = sizeof(AudioStreamBasicDescription);
313   OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc); 
314   if (ret)
315   {
316     CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
317       "Unable to set physical format for stream 0x%04x. Error = %s",
318       (uint)m_StreamId, GetError(ret).c_str());
319     return false;
320   }
321
322   // The AudioStreamSetProperty is not only asynchronious,
323   // it is also not Atomic, in its behaviour.
324   // Therefore we check 5 times before we really give up.
325   // FIXME: failing isn't actually implemented yet.
326   for(int i = 0; i < 10; ++i)
327   {
328     AudioStreamBasicDescription checkPhysicalFormat;
329     if (!GetPhysicalFormat(&checkPhysicalFormat))
330     {
331       CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
332         "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
333       return false;
334     }
335     if (checkPhysicalFormat.mSampleRate == pDesc->mSampleRate &&
336         checkPhysicalFormat.mFormatID   == pDesc->mFormatID   &&
337         checkPhysicalFormat.mFramesPerPacket == pDesc->mFramesPerPacket &&
338         checkPhysicalFormat.mChannelsPerFrame == pDesc->mChannelsPerFrame)
339     {
340       // The right format is now active.
341       CLog::Log(LOGDEBUG, "CCoreAudioStream::SetPhysicalFormat: "
342         "Physical format for stream 0x%04x. now active (%s)",
343         (uint)m_StreamId, StreamDescriptionToString(checkPhysicalFormat, formatString));
344       break;
345     }
346     m_physical_format_event.WaitMSec(100);
347   }
348
349   return true;
350 }
351
352 bool CCoreAudioStream::GetAvailableVirtualFormats(StreamFormatList* pList)
353 {
354   return GetAvailableVirtualFormats(m_StreamId, pList);
355 }
356
357 bool CCoreAudioStream::GetAvailableVirtualFormats(AudioStreamID id, StreamFormatList* pList)
358 {
359   if (!pList || !id)
360     return false;
361
362   AudioObjectPropertyAddress propertyAddress; 
363   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
364   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
365   propertyAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; 
366
367   UInt32 propertySize = 0;
368   OSStatus ret = AudioObjectGetPropertyDataSize(id, &propertyAddress, 0, NULL, &propertySize);
369   if (ret)
370     return false;
371
372   UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
373   AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
374   ret = AudioObjectGetPropertyData(id, &propertyAddress, 0, NULL, &propertySize, pFormatList);
375   if (!ret)
376   {
377     for (UInt32 format = 0; format < formatCount; format++)
378       pList->push_back(pFormatList[format]);
379   }
380   delete[] pFormatList;
381   return (ret == noErr);
382 }
383
384 bool CCoreAudioStream::GetAvailablePhysicalFormats(StreamFormatList* pList)
385 {
386   return GetAvailablePhysicalFormats(m_StreamId, pList);
387 }
388
389 bool CCoreAudioStream::GetAvailablePhysicalFormats(AudioStreamID id, StreamFormatList* pList)
390 {
391   if (!pList || !id)
392     return false;
393
394   AudioObjectPropertyAddress propertyAddress; 
395   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
396   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
397   propertyAddress.mSelector = kAudioStreamPropertyAvailablePhysicalFormats; 
398
399   UInt32 propertySize = 0;
400   OSStatus ret = AudioObjectGetPropertyDataSize(id, &propertyAddress, 0, NULL, &propertySize);
401   if (ret)
402     return false;
403
404   UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
405   AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
406   ret = AudioObjectGetPropertyData(id, &propertyAddress, 0, NULL, &propertySize, pFormatList);
407   if (!ret)
408   {
409     for (UInt32 format = 0; format < formatCount; format++)
410       pList->push_back(pFormatList[format]);
411   }
412   delete[] pFormatList;
413   return (ret == noErr);
414 }
415
416 OSStatus CCoreAudioStream::HardwareStreamListener(AudioObjectID inObjectID,
417   UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
418 {
419   CCoreAudioStream *ca_stream = (CCoreAudioStream*)inClientData;
420
421   for (UInt32 i = 0; i < inNumberAddresses; i++)
422   {
423     if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat)
424     {
425       AudioStreamBasicDescription actualFormat;
426       UInt32 propertySize = sizeof(AudioStreamBasicDescription);
427       // hardware physical format has changed.
428       if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
429       {
430         CStdString formatString;
431         CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
432           "Hardware physical format changed to %s", StreamDescriptionToString(actualFormat, formatString));
433         ca_stream->m_physical_format_event.Set();
434       }
435     }
436     else if (inAddresses[i].mSelector == kAudioStreamPropertyVirtualFormat)
437     {
438       // hardware virtual format has changed.
439       AudioStreamBasicDescription actualFormat;
440       UInt32 propertySize = sizeof(AudioStreamBasicDescription);
441       if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
442       {
443         CStdString formatString;
444         CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
445           "Hardware virtual format changed to %s", StreamDescriptionToString(actualFormat, formatString));
446         ca_stream->m_virtual_format_event.Set();
447       }
448     }
449   }
450
451   return noErr;
452 }