Merge pull request #4196 from arnova/sub_fallback
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / osx / 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 "CoreAudioHelpers.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 void CCoreAudioHardware::ResetAudioDevices()
62 {
63   CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetAudioDevices resetting our devices to LPCM");
64   CoreAudioDeviceList list;
65   if (GetOutputDevices(&list))
66   {
67     for (CoreAudioDeviceList::iterator it = list.begin(); it != list.end(); it ++)
68     {
69       CCoreAudioDevice device = *it;
70
71       AudioStreamIdList streams;
72       if (device.GetStreams(&streams))
73       {
74         CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetAudioDevices %lu streams for device %s", streams.size(), device.GetName().c_str());
75         for (AudioStreamIdList::iterator ait = streams.begin(); ait != streams.end(); ait ++)
76           ResetStream(*ait);
77       }
78     }
79   }
80 }
81
82 void CCoreAudioHardware::ResetStream(AudioStreamID streamId)
83 {
84   CCoreAudioStream stream;
85   stream.Open(streamId);
86   
87   AudioStreamBasicDescription desc;
88   if (stream.GetPhysicalFormat(&desc))
89   {
90     if (desc.mFormatID == 'IAC3' || desc.mFormatID == kAudioFormat60958AC3)
91     {
92       CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetStream stream 0x%x is in encoded format.. setting to LPCM", (unsigned int)streamId);
93
94       StreamFormatList availableFormats;
95       if (stream.GetAvailablePhysicalFormats(&availableFormats))
96       {
97         for (StreamFormatList::iterator fmtIt = availableFormats.begin(); fmtIt != availableFormats.end() ; fmtIt ++)
98         {
99           AudioStreamRangedDescription fmtDesc = *fmtIt;
100           if (fmtDesc.mFormat.mFormatID == kAudioFormatLinearPCM)
101           {
102             AudioStreamBasicDescription newFmt = { 0 };
103             newFmt = fmtDesc.mFormat;
104
105             if (stream.SetPhysicalFormat(&newFmt))
106               break;
107           }
108         }
109       }
110     }
111   }
112
113   stream.Close(false);
114 }
115
116 AudioDeviceID CCoreAudioHardware::FindAudioDevice(const std::string &searchName)
117 {
118   AudioDeviceID deviceId = 0;
119
120   if (!searchName.length())
121     return deviceId;
122
123   std::string searchNameLowerCase = searchName;
124   std::transform(searchNameLowerCase.begin(), searchNameLowerCase.end(), searchNameLowerCase.begin(), ::tolower );
125   if (searchNameLowerCase.compare("default") == 0)
126   {
127     AudioDeviceID defaultDevice = GetDefaultOutputDevice();
128     CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
129       "Returning default device [0x%04x].", (uint)defaultDevice);
130     return defaultDevice;
131   }
132   CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
133     "Searching for device - %s.", searchName.c_str());
134
135   // Obtain a list of all available audio devices
136   AudioObjectPropertyAddress propertyAddress; 
137   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
138   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
139   propertyAddress.mSelector = kAudioHardwarePropertyDevices; 
140
141   UInt32 size = 0;
142   OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size); 
143   if (ret != noErr)
144   {
145     CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
146       "Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
147     return 0;
148   }
149
150   size_t deviceCount = size / sizeof(AudioDeviceID);
151   AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
152   ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
153   if (ret != noErr)
154   {
155     CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
156       "Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
157     delete[] pDevices;
158     return 0;
159   }
160
161   // Attempt to locate the requested device
162   std::string deviceName;
163   for (size_t dev = 0; dev < deviceCount; dev++)
164   {
165     CCoreAudioDevice device;
166     device.Open((pDevices[dev]));
167     deviceName = device.GetName();
168     std::transform( deviceName.begin(), deviceName.end(), deviceName.begin(), ::tolower );
169     if (searchNameLowerCase.compare(deviceName) == 0)
170       deviceId = pDevices[dev];
171     if (deviceId)
172       break;
173   }
174   delete[] pDevices;
175
176   return deviceId;
177 }
178
179 AudioDeviceID CCoreAudioHardware::GetDefaultOutputDevice()
180 {
181   AudioDeviceID deviceId = 0;
182   static AudioDeviceID lastDeviceId = 0;
183
184   AudioObjectPropertyAddress  propertyAddress;
185   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal;
186   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
187   propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
188   
189   UInt32 size = sizeof(AudioDeviceID);
190   OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId);
191
192   // outputDevice is set to 0 if there is no audio device available
193   // or if the default device is set to an encoded format
194   if (ret != noErr || !deviceId) 
195   {
196     CLog::Log(LOGERROR, "CCoreAudioHardware::GetDefaultOutputDevice:"
197       " Unable to identify default output device. Error = %s", GetError(ret).c_str());
198     // if there was no error and no deviceId was returned
199     // return the last known default device
200     if (ret == noErr && !deviceId)
201       return lastDeviceId;
202     else
203       return 0;
204   }
205   
206   lastDeviceId = deviceId;
207
208   return deviceId;
209 }
210
211 void CCoreAudioHardware::GetOutputDeviceName(std::string& name)
212 {
213   name = "Default";
214   AudioDeviceID deviceId = GetDefaultOutputDevice();
215
216   if (deviceId)
217   {
218     AudioObjectPropertyAddress  propertyAddress;
219     propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal;
220     propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
221     propertyAddress.mSelector = kAudioObjectPropertyName;
222
223     CFStringRef theDeviceName = NULL;
224     UInt32 propertySize = sizeof(CFStringRef);
225     OSStatus ret = AudioObjectGetPropertyData(deviceId, &propertyAddress, 0, NULL, &propertySize, &theDeviceName); 
226     if (ret != noErr)
227       return;
228
229     DarwinCFStringRefToUTF8String(theDeviceName, name);
230
231     CFRelease(theDeviceName);
232   }
233 }
234
235 UInt32 CCoreAudioHardware::GetOutputDevices(CoreAudioDeviceList *pList)
236 {
237   UInt32 found = 0;
238   if (!pList)
239     return found;
240
241   // Obtain a list of all available audio devices
242   AudioObjectPropertyAddress propertyAddress; 
243   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal; 
244   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
245   propertyAddress.mSelector = kAudioHardwarePropertyDevices; 
246
247   UInt32 size = 0;
248   OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size); 
249   if (ret != noErr)
250   {
251     CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
252       " Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
253     return found;
254   }
255
256   size_t deviceCount = size / sizeof(AudioDeviceID);
257   AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
258   ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
259   if (ret != noErr)
260     CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
261       " Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
262   else
263   {
264     for (size_t dev = 0; dev < deviceCount; dev++)
265     {
266       CCoreAudioDevice device(pDevices[dev]);
267       if (device.GetTotalOutputChannels() == 0)
268         continue;
269       found++;
270       pList->push_back(pDevices[dev]);
271     }
272   }
273   delete[] pDevices;
274
275   return found;
276 }