[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / CoreAudioHardware.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 "CoreAudioHardware.h"
22
23 #include "CoreAudioAEHAL.h"
24 #include "utils/log.h"
25 #include "osx/DarwinUtils.h"
26
27 bool CCoreAudioHardware::GetAutoHogMode()
28 {
29   AudioObjectPropertyAddress propertyAddress; 
30   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
31   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
32   propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed; 
33
34   UInt32 val = 0;
35   UInt32 size = sizeof(val);
36   OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &val); 
37   if (ret != noErr)
38   {
39     CLog::Log(LOGERROR, "CCoreAudioHardware::GetAutoHogMode: "
40       "Unable to get auto 'hog' mode. Error = %s", GetError(ret).c_str());
41     return false;
42   }
43   return (val == 1);
44 }
45
46 void CCoreAudioHardware::SetAutoHogMode(bool enable)
47 {
48   AudioObjectPropertyAddress propertyAddress; 
49   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
50   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
51   propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed; 
52
53   UInt32 val = enable ? 1 : 0;
54   UInt32 size = sizeof(val);
55   OSStatus ret = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, size, &val); 
56   if (ret != noErr)
57     CLog::Log(LOGERROR, "CCoreAudioHardware::SetAutoHogMode: "
58       "Unable to set auto 'hog' mode. Error = %s", GetError(ret).c_str());
59 }
60
61 AudioStreamBasicDescription* CCoreAudioHardware::FormatsList(AudioStreamID stream)
62 {
63   // Retrieve all the stream formats supported by this output stream
64
65   AudioObjectPropertyAddress propertyAddress; 
66   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
67   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
68   propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormats; 
69
70   UInt32 listSize = 0;
71   OSStatus ret = AudioObjectGetPropertyDataSize(stream, &propertyAddress, 0, NULL, &listSize); 
72   if (ret != noErr)
73   {
74     CLog::Log(LOGDEBUG, "CCoreAudioHardware::FormatsList: "
75       "Unable to get list size. Error = %s", GetError(ret).c_str());
76     return NULL;
77   }
78
79   // Space for a terminating ID:
80   listSize += sizeof(AudioStreamBasicDescription);
81   AudioStreamBasicDescription *list = (AudioStreamBasicDescription*)malloc(listSize);
82   if (list == NULL)
83   {
84     CLog::Log(LOGERROR, "CCoreAudioHardware::FormatsList: Out of memory?");
85     return NULL;
86   }
87
88   ret = AudioObjectGetPropertyData(stream, &propertyAddress, 0, NULL, &listSize, list); 
89   if (ret != noErr)
90   {
91     CLog::Log(LOGDEBUG, "CCoreAudioHardware::FormatsList: "
92       "Unable to get list. Error = %s", GetError(ret).c_str());
93     free(list);
94     return NULL;
95   }
96
97   // Add a terminating ID:
98   list[listSize/sizeof(AudioStreamID)].mFormatID = 0;
99
100   return list;
101 }
102
103 AudioStreamID* CCoreAudioHardware::StreamsList(AudioDeviceID device)
104 {
105   // Get a list of all the streams on this device
106   AudioObjectPropertyAddress  propertyAddress;
107   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal;
108   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
109   propertyAddress.mSelector = kAudioDevicePropertyStreams;
110
111   UInt32 listSize;
112   OSStatus ret = AudioObjectGetPropertyDataSize(device, &propertyAddress, 0, NULL, &listSize); 
113   if (ret != noErr)
114     return NULL;
115
116   // Space for a terminating ID:
117   listSize += sizeof(AudioStreamID);
118   AudioStreamID *list = (AudioStreamID*)malloc(listSize);
119   if (list == NULL)
120   {
121     CLog::Log(LOGERROR, "CCoreAudioHardware::StreamsList: Out of memory?");
122     return NULL;
123   }
124
125   propertyAddress.mScope = kAudioDevicePropertyScopeInput;
126   ret = AudioObjectGetPropertyData(device, &propertyAddress, 0, NULL, &listSize, list);
127   if (ret != noErr)
128   {
129     CLog::Log(LOGERROR, "CCoreAudioHardware::StreamsList: "
130       "Unable to get list. Error = %s", GetError(ret).c_str());
131     return NULL;
132   }
133
134   // Add a terminating ID:
135   list[listSize/sizeof(AudioStreamID)] = kAudioHardwareBadStreamError;
136
137   return list;
138 }
139
140 void CCoreAudioHardware::ResetAudioDevices()
141 {
142   // Reset any devices with an AC3 stream back to a Linear PCM
143   // so that they can become a default output device
144   AudioObjectPropertyAddress propertyAddress; 
145   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
146   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
147   propertyAddress.mSelector = kAudioHardwarePropertyDevices; 
148
149   UInt32 size;
150   OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size); 
151   if (ret != noErr)
152   {
153     CLog::Log(LOGERROR, "CCoreAudioHardware::ResetAudioDevices: ResetAudioDevices - unknown size");
154     return;
155   }
156
157   AudioDeviceID *devices = (AudioDeviceID*)malloc(size);
158   if (!devices)
159   {
160     CLog::Log(LOGERROR, "CCoreAudioHardware::ResetAudioDevices: ResetAudioDevices - out of memory?");
161     return;
162   }
163   int numDevices = size / sizeof(AudioDeviceID);
164   ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, devices); 
165   if (ret != noErr)
166   {
167     CLog::Log(LOGERROR, "CCoreAudioHardware::ResetAudioDevices: ResetAudioDevices - cannot get device list");
168     return;
169   }
170
171   for (int i = 0; i < numDevices; i++)
172   {
173     AudioStreamID *streams = StreamsList(devices[i]);
174     if (streams)
175     {
176       for (int j = 0; streams[j] != kAudioHardwareBadStreamError; j++)
177         ResetStream(streams[j]);
178       free(streams);
179     }
180   }
181   free(devices);
182 }
183
184 void CCoreAudioHardware::ResetStream(AudioStreamID stream)
185 {
186   // Find the streams current physical format
187   AudioObjectPropertyAddress propertyAddress; 
188   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
189   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
190   propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat; 
191
192   AudioStreamBasicDescription currentFormat;
193   UInt32 paramSize = sizeof(currentFormat);
194   OSStatus ret = AudioObjectGetPropertyData(stream, &propertyAddress, 0, NULL, &paramSize, &currentFormat);
195   if (ret != noErr)
196     return;
197
198   // If it's currently AC-3/SPDIF then reset it to some mixable format
199   if (currentFormat.mFormatID == 'IAC3' ||
200       currentFormat.mFormatID == kAudioFormat60958AC3)
201   {
202     AudioStreamBasicDescription *formats = CCoreAudioHardware::FormatsList(stream);
203     bool streamReset = false;
204
205     if (!formats)
206       return;
207
208     for (int i = 0; !streamReset && formats[i].mFormatID != 0; i++)
209     {
210       if (formats[i].mFormatID == kAudioFormatLinearPCM)
211       {
212         ret = AudioObjectSetPropertyData(stream, &propertyAddress, 0, NULL, sizeof(formats[i]), &(formats[i])); 
213         if (ret != noErr)
214         {
215           CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetStream: "
216             "Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
217           continue;
218         }
219         else
220         {
221           streamReset = true;
222           Sleep(10);
223         }
224       }
225     }
226     free(formats);
227   }
228 }
229
230 AudioDeviceID CCoreAudioHardware::FindAudioDevice(const std::string &searchName)
231 {
232   AudioDeviceID deviceId = 0;
233
234   if (!searchName.length())
235     return deviceId;
236
237   std::string searchNameLowerCase = searchName;
238   std::transform(searchNameLowerCase.begin(), searchNameLowerCase.end(), searchNameLowerCase.begin(), ::tolower );
239   if (searchNameLowerCase.compare("default") == 0)
240   {
241     AudioDeviceID defaultDevice = GetDefaultOutputDevice();
242     CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
243       "Returning default device [0x%04x].", (uint)defaultDevice);
244     return defaultDevice;
245   }
246   CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
247     "Searching for device - %s.", searchName.c_str());
248
249   // Obtain a list of all available audio devices
250   AudioObjectPropertyAddress propertyAddress; 
251   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
252   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
253   propertyAddress.mSelector = kAudioHardwarePropertyDevices; 
254
255   UInt32 size = 0;
256   OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size); 
257   if (ret != noErr)
258   {
259     CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
260       "Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
261     return 0;
262   }
263
264   size_t deviceCount = size / sizeof(AudioDeviceID);
265   AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
266   ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
267   if (ret != noErr)
268   {
269     CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
270       "Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
271     delete[] pDevices;
272     return 0;
273   }
274
275   // Attempt to locate the requested device
276   std::string deviceName;
277   for (size_t dev = 0; dev < deviceCount; dev++)
278   {
279     CCoreAudioDevice device;
280     device.Open((pDevices[dev]));
281     deviceName = device.GetName();
282     std::transform( deviceName.begin(), deviceName.end(), deviceName.begin(), ::tolower );
283     if (searchNameLowerCase.compare(deviceName) == 0)
284       deviceId = pDevices[dev];
285     if (deviceId)
286       break;
287   }
288   delete[] pDevices;
289
290   return deviceId;
291 }
292
293 AudioDeviceID CCoreAudioHardware::GetDefaultOutputDevice()
294 {
295   AudioDeviceID deviceId = 0;
296   static AudioDeviceID lastDeviceId = 0;
297
298   AudioObjectPropertyAddress  propertyAddress;
299   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal;
300   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
301   propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
302   
303   UInt32 size = sizeof(AudioDeviceID);
304   OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId);
305
306   // outputDevice is set to 0 if there is no audio device available
307   // or if the default device is set to an encoded format
308   if (ret != noErr || !deviceId) 
309   {
310     CLog::Log(LOGERROR, "CCoreAudioHardware::GetDefaultOutputDevice:"
311       " Unable to identify default output device. Error = %s", GetError(ret).c_str());
312     // if there was no error and no deviceId was returned
313     // return the last known default device
314     if (ret == noErr && !deviceId)
315       return lastDeviceId;
316     else
317       return 0;
318   }
319   
320   lastDeviceId = deviceId;
321
322   return deviceId;
323 }
324
325 void CCoreAudioHardware::GetOutputDeviceName(std::string& name)
326 {
327   name = "Default";
328   AudioDeviceID deviceId = GetDefaultOutputDevice();
329
330   if (deviceId)
331   {
332     AudioObjectPropertyAddress  propertyAddress;
333     propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal;
334     propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
335     propertyAddress.mSelector = kAudioObjectPropertyName;
336
337     CFStringRef theDeviceName = NULL;
338     UInt32 propertySize = sizeof(CFStringRef);
339     OSStatus ret = AudioObjectGetPropertyData(deviceId, &propertyAddress, 0, NULL, &propertySize, &theDeviceName); 
340     if (ret != noErr)
341       return;
342
343     DarwinCFStringRefToString(theDeviceName, name);
344
345     CFRelease(theDeviceName);
346   }
347 }
348
349 UInt32 CCoreAudioHardware::GetOutputDevices(CoreAudioDeviceList *pList)
350 {
351   UInt32 found = 0;
352   if (!pList)
353     return found;
354
355   // Obtain a list of all available audio devices
356   AudioObjectPropertyAddress propertyAddress; 
357   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
358   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
359   propertyAddress.mSelector = kAudioHardwarePropertyDevices; 
360
361   UInt32 size = 0;
362   OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size); 
363   if (ret != noErr)
364   {
365     CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
366       " Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
367     return found;
368   }
369
370   size_t deviceCount = size / sizeof(AudioDeviceID);
371   AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
372   ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
373   if (ret != noErr)
374     CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
375       " Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
376   else
377   {
378     for (size_t dev = 0; dev < deviceCount; dev++)
379     {
380       CCoreAudioDevice device(pDevices[dev]);
381       if (device.GetTotalOutputChannels() == 0)
382         continue;
383       found++;
384       pList->push_back(pDevices[dev]);
385     }
386   }
387   delete[] pDevices;
388
389   return found;
390 }