[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / 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 "CoreAudioAEHAL.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()
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 (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 (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 UInt32 CCoreAudioStream::GetTerminalType()
131 {
132   if (!m_StreamId)
133     return 0;
134
135   UInt32 val = 0;
136   UInt32 size = sizeof(UInt32);
137
138   AudioObjectPropertyAddress propertyAddress; 
139   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
140   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
141   propertyAddress.mSelector = kAudioStreamPropertyTerminalType; 
142
143   OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, &val); 
144   if (ret)
145     return 0;
146   return val;
147 }
148
149 UInt32 CCoreAudioStream::GetNumLatencyFrames()
150 {
151   if (!m_StreamId)
152     return 0;
153
154   UInt32 i_param_size = sizeof(uint32_t);
155   UInt32 i_param, num_latency_frames = 0;
156
157   AudioObjectPropertyAddress propertyAddress; 
158   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
159   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
160   propertyAddress.mSelector = kAudioStreamPropertyLatency; 
161
162   // number of frames of latency in the AudioStream
163   OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &i_param_size, &i_param); 
164   if (ret == noErr)
165   {
166     num_latency_frames += i_param;
167   }
168
169   return num_latency_frames;
170 }
171
172 bool CCoreAudioStream::GetVirtualFormat(AudioStreamBasicDescription* pDesc)
173 {
174   if (!pDesc || !m_StreamId)
175     return false;
176
177   UInt32 size = sizeof(AudioStreamBasicDescription);
178
179   AudioObjectPropertyAddress propertyAddress; 
180   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
181   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
182   propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat; 
183   OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &size); 
184   if (ret)
185     return false;
186
187   ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, pDesc); 
188   if (ret)
189     return false;
190   return true;
191 }
192
193 bool CCoreAudioStream::SetVirtualFormat(AudioStreamBasicDescription* pDesc)
194 {
195   if (!pDesc || !m_StreamId)
196     return false;
197
198   std::string formatString;
199
200   if (!m_OriginalVirtualFormat.mFormatID)
201   {
202     // Store the original format (as we found it) so that it can be restored later
203     if (!GetVirtualFormat(&m_OriginalVirtualFormat))
204     {
205       CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
206         "Unable to retrieve current virtual format for stream 0x%04x.", (uint)m_StreamId);
207       return false;
208     }
209   }
210   m_virtual_format_event.Reset();
211
212   AudioObjectPropertyAddress propertyAddress; 
213   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
214   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
215   propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat; 
216
217   UInt32 propertySize = sizeof(AudioStreamBasicDescription);
218   OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc); 
219   if (ret)
220   {
221     CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
222       "Unable to set virtual format for stream 0x%04x. Error = %s",
223       (uint)m_StreamId, GetError(ret).c_str());
224     return false;
225   }
226
227   // The AudioStreamSetProperty is not only asynchronious,
228   // it is also not Atomic, in its behaviour.
229   // Therefore we check 5 times before we really give up.
230   // FIXME: failing isn't actually implemented yet.
231   for (int i = 0; i < 10; ++i)
232   {
233     AudioStreamBasicDescription checkVirtualFormat;
234     if (!GetVirtualFormat(&checkVirtualFormat))
235     {
236       CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
237         "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
238       return false;
239     }
240     if (checkVirtualFormat.mSampleRate == pDesc->mSampleRate &&
241         checkVirtualFormat.mFormatID == pDesc->mFormatID &&
242         checkVirtualFormat.mFramesPerPacket == pDesc->mFramesPerPacket)
243     {
244       // The right format is now active.
245       CLog::Log(LOGDEBUG, "CCoreAudioStream::SetVirtualFormat: "
246         "Virtual format for stream 0x%04x. now active (%s)",
247         (uint)m_StreamId, StreamDescriptionToString(checkVirtualFormat, formatString));
248       break;
249     }
250     m_virtual_format_event.WaitMSec(100);
251   }
252   return true;
253 }
254
255 bool CCoreAudioStream::GetPhysicalFormat(AudioStreamBasicDescription* pDesc)
256 {
257   if (!pDesc || !m_StreamId)
258     return false;
259
260   UInt32 size = sizeof(AudioStreamBasicDescription);
261
262   AudioObjectPropertyAddress propertyAddress; 
263   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
264   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
265   propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat; 
266
267   OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, pDesc); 
268   if (ret)
269     return false;
270   return true;
271 }
272
273 bool CCoreAudioStream::SetPhysicalFormat(AudioStreamBasicDescription* pDesc)
274 {
275   if (!pDesc || !m_StreamId)
276     return false;
277
278   std::string formatString;
279
280   if (!m_OriginalPhysicalFormat.mFormatID)
281   {
282     // Store the original format (as we found it) so that it can be restored later
283     if (!GetPhysicalFormat(&m_OriginalPhysicalFormat))
284     {
285       CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
286         "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
287       return false;
288     }
289   }
290   m_physical_format_event.Reset();
291
292   AudioObjectPropertyAddress propertyAddress; 
293   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
294   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
295   propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat; 
296
297   UInt32 propertySize = sizeof(AudioStreamBasicDescription);
298   OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc); 
299   if (ret)
300   {
301     CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
302       "Unable to set physical format for stream 0x%04x. Error = %s",
303       (uint)m_StreamId, GetError(ret).c_str());
304     return false;
305   }
306
307   // The AudioStreamSetProperty is not only asynchronious,
308   // it is also not Atomic, in its behaviour.
309   // Therefore we check 5 times before we really give up.
310   // FIXME: failing isn't actually implemented yet.
311   for(int i = 0; i < 10; ++i)
312   {
313     AudioStreamBasicDescription checkPhysicalFormat;
314     if (!GetPhysicalFormat(&checkPhysicalFormat))
315     {
316       CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
317         "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
318       return false;
319     }
320     if (checkPhysicalFormat.mSampleRate == pDesc->mSampleRate &&
321         checkPhysicalFormat.mFormatID   == pDesc->mFormatID   &&
322         checkPhysicalFormat.mFramesPerPacket == pDesc->mFramesPerPacket)
323     {
324       // The right format is now active.
325       CLog::Log(LOGDEBUG, "CCoreAudioStream::SetPhysicalFormat: "
326         "Physical format for stream 0x%04x. now active (%s)",
327         (uint)m_StreamId, StreamDescriptionToString(checkPhysicalFormat, formatString));
328       break;
329     }
330     m_physical_format_event.WaitMSec(100);
331   }
332
333   return true;
334 }
335
336 bool CCoreAudioStream::GetAvailableVirtualFormats(StreamFormatList* pList)
337 {
338   if (!pList || !m_StreamId)
339     return false;
340
341   AudioObjectPropertyAddress propertyAddress; 
342   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
343   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
344   propertyAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; 
345
346   UInt32 propertySize = 0;
347   OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &propertySize); 
348   if (ret)
349     return false;
350
351   UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
352   AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
353   ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &propertySize, pFormatList); 
354   if (!ret)
355   {
356     for (UInt32 format = 0; format < formatCount; format++)
357       pList->push_back(pFormatList[format]);
358   }
359   delete[] pFormatList;
360   return (ret == noErr);
361 }
362
363 bool CCoreAudioStream::GetAvailablePhysicalFormats(StreamFormatList* pList)
364 {
365   if (!pList || !m_StreamId)
366     return false;
367
368   AudioObjectPropertyAddress propertyAddress; 
369   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
370   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
371   propertyAddress.mSelector = kAudioStreamPropertyAvailablePhysicalFormats; 
372
373   UInt32 propertySize = 0;
374   OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &propertySize); 
375   if (ret)
376     return false;
377
378   UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
379   AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
380   ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &propertySize, pFormatList); 
381   if (!ret)
382   {
383     for (UInt32 format = 0; format < formatCount; format++)
384       pList->push_back(pFormatList[format]);
385   }
386   delete[] pFormatList;
387   return (ret == noErr);
388 }
389
390 OSStatus CCoreAudioStream::HardwareStreamListener(AudioObjectID inObjectID,
391   UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
392 {
393   CCoreAudioStream *ca_stream = (CCoreAudioStream*)inClientData;
394
395   for (UInt32 i = 0; i < inNumberAddresses; i++)
396   {
397     if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat)
398     {
399       AudioStreamBasicDescription actualFormat;
400       UInt32 propertySize = sizeof(AudioStreamBasicDescription);
401       // hardware physical format has changed.
402       if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
403       {
404         CStdString formatString;
405         CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
406           "Hardware physical format changed to %s", StreamDescriptionToString(actualFormat, formatString));
407         ca_stream->m_physical_format_event.Set();
408       }
409     }
410     else if (inAddresses[i].mSelector == kAudioStreamPropertyVirtualFormat)
411     {
412       // hardware virtual format has changed.
413       AudioStreamBasicDescription actualFormat;
414       UInt32 propertySize = sizeof(AudioStreamBasicDescription);
415       if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
416       {
417         CStdString formatString;
418         CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
419           "Hardware virtual format changed to %s", StreamDescriptionToString(actualFormat, formatString));
420         ca_stream->m_virtual_format_event.Set();
421       }
422     }
423   }
424
425   return noErr;
426 }