2 * Copyright (C) 2011-2013 Team XBMC
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)
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.
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/>.
21 #include "CoreAudioHardware.h"
23 #include "CoreAudioAEHAL.h"
24 #include "utils/log.h"
25 #include "osx/DarwinUtils.h"
27 bool CCoreAudioHardware::GetAutoHogMode()
29 AudioObjectPropertyAddress propertyAddress;
30 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
31 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
32 propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed;
35 UInt32 size = sizeof(val);
36 OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &val);
39 CLog::Log(LOGERROR, "CCoreAudioHardware::GetAutoHogMode: "
40 "Unable to get auto 'hog' mode. Error = %s", GetError(ret).c_str());
46 void CCoreAudioHardware::SetAutoHogMode(bool enable)
48 AudioObjectPropertyAddress propertyAddress;
49 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
50 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
51 propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed;
53 UInt32 val = enable ? 1 : 0;
54 UInt32 size = sizeof(val);
55 OSStatus ret = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, size, &val);
57 CLog::Log(LOGERROR, "CCoreAudioHardware::SetAutoHogMode: "
58 "Unable to set auto 'hog' mode. Error = %s", GetError(ret).c_str());
61 AudioStreamBasicDescription* CCoreAudioHardware::FormatsList(AudioStreamID stream)
63 // Retrieve all the stream formats supported by this output stream
65 AudioObjectPropertyAddress propertyAddress;
66 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
67 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
68 propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormats;
71 OSStatus ret = AudioObjectGetPropertyDataSize(stream, &propertyAddress, 0, NULL, &listSize);
74 CLog::Log(LOGDEBUG, "CCoreAudioHardware::FormatsList: "
75 "Unable to get list size. Error = %s", GetError(ret).c_str());
79 // Space for a terminating ID:
80 listSize += sizeof(AudioStreamBasicDescription);
81 AudioStreamBasicDescription *list = (AudioStreamBasicDescription*)malloc(listSize);
84 CLog::Log(LOGERROR, "CCoreAudioHardware::FormatsList: Out of memory?");
88 ret = AudioObjectGetPropertyData(stream, &propertyAddress, 0, NULL, &listSize, list);
91 CLog::Log(LOGDEBUG, "CCoreAudioHardware::FormatsList: "
92 "Unable to get list. Error = %s", GetError(ret).c_str());
97 // Add a terminating ID:
98 list[listSize/sizeof(AudioStreamID)].mFormatID = 0;
103 AudioStreamID* CCoreAudioHardware::StreamsList(AudioDeviceID device)
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;
112 OSStatus ret = AudioObjectGetPropertyDataSize(device, &propertyAddress, 0, NULL, &listSize);
116 // Space for a terminating ID:
117 listSize += sizeof(AudioStreamID);
118 AudioStreamID *list = (AudioStreamID*)malloc(listSize);
121 CLog::Log(LOGERROR, "CCoreAudioHardware::StreamsList: Out of memory?");
125 propertyAddress.mScope = kAudioDevicePropertyScopeInput;
126 ret = AudioObjectGetPropertyData(device, &propertyAddress, 0, NULL, &listSize, list);
129 CLog::Log(LOGERROR, "CCoreAudioHardware::StreamsList: "
130 "Unable to get list. Error = %s", GetError(ret).c_str());
134 // Add a terminating ID:
135 list[listSize/sizeof(AudioStreamID)] = kAudioHardwareBadStreamError;
140 void CCoreAudioHardware::ResetAudioDevices()
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;
150 OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
153 CLog::Log(LOGERROR, "CCoreAudioHardware::ResetAudioDevices: ResetAudioDevices - unknown size");
157 AudioDeviceID *devices = (AudioDeviceID*)malloc(size);
160 CLog::Log(LOGERROR, "CCoreAudioHardware::ResetAudioDevices: ResetAudioDevices - out of memory?");
163 int numDevices = size / sizeof(AudioDeviceID);
164 ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, devices);
167 CLog::Log(LOGERROR, "CCoreAudioHardware::ResetAudioDevices: ResetAudioDevices - cannot get device list");
171 for (int i = 0; i < numDevices; i++)
173 AudioStreamID *streams = StreamsList(devices[i]);
176 for (int j = 0; streams[j] != kAudioHardwareBadStreamError; j++)
177 ResetStream(streams[j]);
184 void CCoreAudioHardware::ResetStream(AudioStreamID stream)
186 // Find the streams current physical format
187 AudioObjectPropertyAddress propertyAddress;
188 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
189 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
190 propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
192 AudioStreamBasicDescription currentFormat;
193 UInt32 paramSize = sizeof(currentFormat);
194 OSStatus ret = AudioObjectGetPropertyData(stream, &propertyAddress, 0, NULL, ¶mSize, ¤tFormat);
198 // If it's currently AC-3/SPDIF then reset it to some mixable format
199 if (currentFormat.mFormatID == 'IAC3' ||
200 currentFormat.mFormatID == kAudioFormat60958AC3)
202 AudioStreamBasicDescription *formats = CCoreAudioHardware::FormatsList(stream);
203 bool streamReset = false;
208 for (int i = 0; !streamReset && formats[i].mFormatID != 0; i++)
210 if (formats[i].mFormatID == kAudioFormatLinearPCM)
212 ret = AudioObjectSetPropertyData(stream, &propertyAddress, 0, NULL, sizeof(formats[i]), &(formats[i]));
215 CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetStream: "
216 "Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
230 AudioDeviceID CCoreAudioHardware::FindAudioDevice(const std::string &searchName)
232 AudioDeviceID deviceId = 0;
234 if (!searchName.length())
237 std::string searchNameLowerCase = searchName;
238 std::transform(searchNameLowerCase.begin(), searchNameLowerCase.end(), searchNameLowerCase.begin(), ::tolower );
239 if (searchNameLowerCase.compare("default") == 0)
241 AudioDeviceID defaultDevice = GetDefaultOutputDevice();
242 CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
243 "Returning default device [0x%04x].", (uint)defaultDevice);
244 return defaultDevice;
246 CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
247 "Searching for device - %s.", searchName.c_str());
249 // Obtain a list of all available audio devices
250 AudioObjectPropertyAddress propertyAddress;
251 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
252 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
253 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
256 OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
259 CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
260 "Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
264 size_t deviceCount = size / sizeof(AudioDeviceID);
265 AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
266 ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
269 CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
270 "Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
275 // Attempt to locate the requested device
276 std::string deviceName;
277 for (size_t dev = 0; dev < deviceCount; dev++)
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];
293 AudioDeviceID CCoreAudioHardware::GetDefaultOutputDevice()
295 AudioDeviceID deviceId = 0;
296 static AudioDeviceID lastDeviceId = 0;
298 AudioObjectPropertyAddress propertyAddress;
299 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
300 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
301 propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
303 UInt32 size = sizeof(AudioDeviceID);
304 OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId);
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)
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)
320 lastDeviceId = deviceId;
325 void CCoreAudioHardware::GetOutputDeviceName(std::string& name)
328 AudioDeviceID deviceId = GetDefaultOutputDevice();
332 AudioObjectPropertyAddress propertyAddress;
333 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
334 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
335 propertyAddress.mSelector = kAudioObjectPropertyName;
337 CFStringRef theDeviceName = NULL;
338 UInt32 propertySize = sizeof(CFStringRef);
339 OSStatus ret = AudioObjectGetPropertyData(deviceId, &propertyAddress, 0, NULL, &propertySize, &theDeviceName);
343 DarwinCFStringRefToString(theDeviceName, name);
345 CFRelease(theDeviceName);
349 UInt32 CCoreAudioHardware::GetOutputDevices(CoreAudioDeviceList *pList)
355 // Obtain a list of all available audio devices
356 AudioObjectPropertyAddress propertyAddress;
357 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
358 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
359 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
362 OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
365 CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
366 " Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
370 size_t deviceCount = size / sizeof(AudioDeviceID);
371 AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
372 ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
374 CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
375 " Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
378 for (size_t dev = 0; dev < deviceCount; dev++)
380 CCoreAudioDevice device(pDevices[dev]);
381 if (device.GetTotalOutputChannels() == 0)
384 pList->push_back(pDevices[dev]);