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 "CoreAudioDevice.h"
22 #include "CoreAudioHelpers.h"
23 #include "CoreAudioChannelLayout.h"
24 #include "CoreAudioHardware.h"
25 #include "utils/log.h"
27 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
29 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
30 CCoreAudioDevice::CCoreAudioDevice() :
35 m_ObjectListenerProc (NULL ),
36 m_SampleRateRestore (0.0f ),
39 m_OutputBufferIndex (0 ),
40 m_BufferSizeRestore (0 )
44 CCoreAudioDevice::CCoreAudioDevice(AudioDeviceID deviceId) :
46 m_DeviceId (deviceId ),
49 m_ObjectListenerProc (NULL ),
50 m_SampleRateRestore (0.0f ),
53 m_OutputBufferIndex (0 ),
54 m_BufferSizeRestore (0 )
58 CCoreAudioDevice::~CCoreAudioDevice()
63 bool CCoreAudioDevice::Open(AudioDeviceID deviceId)
65 m_DeviceId = deviceId;
66 m_BufferSizeRestore = GetBufferSize();
70 void CCoreAudioDevice::Close()
75 // Stop the device if it was started
78 // Unregister the IOProc if we have one
82 CCoreAudioHardware::SetAutoHogMode(false);
84 if (m_MixerRestore > -1) // We changed the mixer status
85 SetMixingSupport((m_MixerRestore ? true : false));
88 if (m_SampleRateRestore != 0.0f)
89 SetNominalSampleRate(m_SampleRateRestore);
91 if (m_BufferSizeRestore && m_BufferSizeRestore != GetBufferSize())
93 SetBufferSize(m_BufferSizeRestore);
94 m_BufferSizeRestore = 0;
99 m_ObjectListenerProc = NULL;
102 void CCoreAudioDevice::Start()
104 if (!m_DeviceId || m_Started)
107 OSStatus ret = AudioDeviceStart(m_DeviceId, m_IoProc);
109 CLog::Log(LOGERROR, "CCoreAudioDevice::Start: "
110 "Unable to start device. Error = %s", GetError(ret).c_str());
115 void CCoreAudioDevice::Stop()
117 if (!m_DeviceId || !m_Started)
120 OSStatus ret = AudioDeviceStop(m_DeviceId, m_IoProc);
122 CLog::Log(LOGERROR, "CCoreAudioDevice::Stop: "
123 "Unable to stop device. Error = %s", GetError(ret).c_str());
127 void CCoreAudioDevice::RemoveObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
132 AudioObjectPropertyAddress audioProperty;
133 audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
134 audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
135 audioProperty.mElement = kAudioObjectPropertyElementWildcard;
137 OSStatus ret = AudioObjectRemovePropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
140 CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveObjectListenerProc: "
141 "Unable to set ObjectListener callback. Error = %s", GetError(ret).c_str());
143 m_ObjectListenerProc = NULL;
146 bool CCoreAudioDevice::SetObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
148 // Allow only one ObjectListener at a time
149 if (!m_DeviceId || m_ObjectListenerProc)
152 AudioObjectPropertyAddress audioProperty;
153 audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
154 audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
155 audioProperty.mElement = kAudioObjectPropertyElementWildcard;
157 OSStatus ret = AudioObjectAddPropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
161 CLog::Log(LOGERROR, "CCoreAudioDevice::SetObjectListenerProc: "
162 "Unable to remove ObjectListener callback. Error = %s", GetError(ret).c_str());
166 m_ObjectListenerProc = callback;
169 bool CCoreAudioDevice::AddIOProc(AudioDeviceIOProc ioProc, void* pCallbackData)
171 // Allow only one IOProc at a time
172 if (!m_DeviceId || m_IoProc)
175 OSStatus ret = AudioDeviceCreateIOProcID(m_DeviceId, ioProc, pCallbackData, &m_IoProc);
178 CLog::Log(LOGERROR, "CCoreAudioDevice::AddIOProc: "
179 "Unable to add IOProc. Error = %s", GetError(ret).c_str());
189 bool CCoreAudioDevice::RemoveIOProc()
191 if (!m_DeviceId || !m_IoProc)
196 OSStatus ret = AudioDeviceDestroyIOProcID(m_DeviceId, m_IoProc);
198 CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveIOProc: "
199 "Unable to remove IOProc. Error = %s", GetError(ret).c_str());
201 m_IoProc = NULL; // Clear the reference no matter what
208 std::string CCoreAudioDevice::GetName()
213 AudioObjectPropertyAddress propertyAddress;
214 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
215 propertyAddress.mElement = 0;
216 propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
219 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
224 char *buff = new char[propertySize + 1];
225 buff[propertySize] = 0x00;
226 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, buff);
229 CLog::Log(LOGERROR, "CCoreAudioDevice::GetName: "
230 "Unable to get device name - id: 0x%04x. Error = %s", (uint)m_DeviceId, GetError(ret).c_str());
235 name.erase(name.find_last_not_of(" ") + 1);
242 bool CCoreAudioDevice::IsDigital(UInt32 &transportType)
244 bool isDigital = false;
248 AudioObjectPropertyAddress propertyAddress;
249 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
250 propertyAddress.mElement = 0;
251 propertyAddress.mSelector = kAudioDevicePropertyTransportType;
253 UInt32 propertySize = sizeof(transportType);
254 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &transportType);
258 if (transportType == kIOAudioDeviceTransportTypeFireWire)
260 if (transportType == kIOAudioDeviceTransportTypeUSB)
262 if (transportType == kIOAudioDeviceTransportTypeHdmi)
264 if (transportType == kIOAudioDeviceTransportTypeDisplayPort)
266 if (transportType == kIOAudioDeviceTransportTypeThunderbolt)
268 if (transportType == kAudioStreamTerminalTypeDigitalAudioInterface)
274 UInt32 CCoreAudioDevice::GetTotalOutputChannels()
281 AudioObjectPropertyAddress propertyAddress;
282 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
283 propertyAddress.mElement = 0;
284 propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
287 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &size);
291 AudioBufferList* pList = (AudioBufferList*)malloc(size);
292 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, pList);
295 for(UInt32 buffer = 0; buffer < pList->mNumberBuffers; ++buffer)
296 channels += pList->mBuffers[buffer].mNumberChannels;
300 CLog::Log(LOGERROR, "CCoreAudioDevice::GetTotalOutputChannels: "
301 "Unable to get total device output channels - id: 0x%04x. Error = %s",
302 (uint)m_DeviceId, GetError(ret).c_str());
310 bool CCoreAudioDevice::GetStreams(AudioStreamIdList* pList)
312 if (!pList || !m_DeviceId)
315 AudioObjectPropertyAddress propertyAddress;
316 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
317 propertyAddress.mElement = 0;
318 propertyAddress.mSelector = kAudioDevicePropertyStreams;
320 UInt32 propertySize = 0;
321 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
325 UInt32 streamCount = propertySize / sizeof(AudioStreamID);
326 AudioStreamID* pStreamList = new AudioStreamID[streamCount];
327 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pStreamList);
330 for (UInt32 stream = 0; stream < streamCount; stream++)
331 pList->push_back(pStreamList[stream]);
333 delete[] pStreamList;
339 bool CCoreAudioDevice::IsRunning()
341 AudioObjectPropertyAddress propertyAddress;
342 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
343 propertyAddress.mElement = 0;
344 propertyAddress.mSelector = kAudioDevicePropertyDeviceIsRunning;
346 UInt32 isRunning = 0;
347 UInt32 propertySize = sizeof(isRunning);
348 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &isRunning);
352 return isRunning != 0;
355 bool CCoreAudioDevice::SetHogStatus(bool hog)
357 // According to Jeff Moore (Core Audio, Apple), Setting kAudioDevicePropertyHogMode
358 // is a toggle and the only way to tell if you do get hog mode is to compare
359 // the returned pid against getpid, if the match, you have hog mode, if not you don't.
363 AudioObjectPropertyAddress propertyAddress;
364 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
365 propertyAddress.mElement = 0;
366 propertyAddress.mSelector = kAudioDevicePropertyHogMode;
373 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(m_HogPid), &m_HogPid);
375 // even if setting hogmode was successfull our PID might not get written
376 // into m_HogPid (so it stays -1). Readback hogstatus for judging if we
377 // had success on getting hog status
378 // We do this only when AudioObjectSetPropertyData didn't set m_HogPid because
379 // it seems that in the other cases the GetHogStatus could return -1
380 // which would overwrite our valid m_HogPid again
381 // Man we should never touch this shit again ;)
383 m_HogPid = GetHogStatus();
385 if (ret || m_HogPid != getpid())
387 CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
388 "Unable to set 'hog' status. Error = %s", GetError(ret).c_str());
399 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(hogPid), &hogPid);
400 if (ret || hogPid == getpid())
402 CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
403 "Unable to release 'hog' status. Error = %s", GetError(ret).c_str());
406 // Reset internal state
413 pid_t CCoreAudioDevice::GetHogStatus()
418 AudioObjectPropertyAddress propertyAddress;
419 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
420 propertyAddress.mElement = 0;
421 propertyAddress.mSelector = kAudioDevicePropertyHogMode;
424 UInt32 size = sizeof(hogPid);
425 AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &hogPid);
430 bool CCoreAudioDevice::SetMixingSupport(UInt32 mix)
435 if (!GetMixingSupport())
439 if (m_MixerRestore == -1)
441 // This is our first change to this setting. Store the original setting for restore
442 restore = (GetMixingSupport() ? 1 : 0);
445 AudioObjectPropertyAddress propertyAddress;
446 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
447 propertyAddress.mElement = 0;
448 propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
450 UInt32 mixEnable = mix ? 1 : 0;
451 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(mixEnable), &mixEnable);
454 CLog::Log(LOGERROR, "CCoreAudioDevice::SetMixingSupport: "
455 "Unable to set MixingSupport to %s. Error = %s", mix ? "'On'" : "'Off'", GetError(ret).c_str());
458 if (m_MixerRestore == -1)
459 m_MixerRestore = restore;
463 bool CCoreAudioDevice::GetMixingSupport()
470 Boolean writable = false;
472 AudioObjectPropertyAddress propertyAddress;
473 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
474 propertyAddress.mElement = 0;
475 propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
477 if( AudioObjectHasProperty( m_DeviceId, &propertyAddress ) )
479 OSStatus ret = AudioObjectIsPropertySettable(m_DeviceId, &propertyAddress, &writable);
482 CLog::Log(LOGERROR, "CCoreAudioDevice::SupportsMixing: "
483 "Unable to get propertyinfo mixing support. Error = %s", GetError(ret).c_str());
490 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &mix);
495 CLog::Log(LOGDEBUG, "CCoreAudioDevice::SupportsMixing: "
496 "Device mixing support : %s.", mix ? "'Yes'" : "'No'");
501 bool CCoreAudioDevice::SetCurrentVolume(Float32 vol)
506 AudioObjectPropertyAddress propertyAddress;
507 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
508 propertyAddress.mElement = 0;
509 propertyAddress.mSelector = kHALOutputParam_Volume;
511 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float32), &vol);
514 CLog::Log(LOGERROR, "CCoreAudioDevice::SetCurrentVolume: "
515 "Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
521 bool CCoreAudioDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
526 AudioObjectPropertyAddress propertyAddress;
527 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
528 propertyAddress.mElement = 0;
529 propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
531 UInt32 propertySize = 0;
532 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
536 void* pBuf = malloc(propertySize);
537 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf);
539 CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: "
540 "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
543 // Copy the result into the caller's instance
544 layout.CopyLayout(*((AudioChannelLayout*)pBuf));
547 return (ret == noErr);
550 bool CCoreAudioDevice::GetPreferredChannelLayoutForStereo(CCoreAudioChannelLayout &layout)
555 AudioObjectPropertyAddress propertyAddress;
556 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
557 propertyAddress.mElement = 0;
558 propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelsForStereo;
560 UInt32 channels[2];// this will receive the channel labels
561 UInt32 propertySize = sizeof(channels);
563 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &channels);
565 CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayoutForStereo: "
566 "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
569 // Copy/generate a layout into the result into the caller's instance
570 layout.CopyLayoutForStereo(channels);
572 return (ret == noErr);
575 bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList)
577 if (!pList || !m_DeviceId)
580 AudioObjectPropertyAddress propertyAddress;
581 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
582 propertyAddress.mElement = 0;
583 propertyAddress.mSelector = kAudioDevicePropertyDataSources;
585 UInt32 propertySize = 0;
586 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
590 UInt32 sources = propertySize / sizeof(UInt32);
591 UInt32* pSources = new UInt32[sources];
592 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pSources);
595 for (UInt32 i = 0; i < sources; i++)
596 pList->push_back(pSources[i]);
602 Float64 CCoreAudioDevice::GetNominalSampleRate()
607 AudioObjectPropertyAddress propertyAddress;
608 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
609 propertyAddress.mElement = 0;
610 propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
612 Float64 sampleRate = 0.0f;
613 UInt32 propertySize = sizeof(Float64);
614 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &sampleRate);
617 CLog::Log(LOGERROR, "CCoreAudioDevice::GetNominalSampleRate: "
618 "Unable to retrieve current device sample rate. Error = %s", GetError(ret).c_str());
624 bool CCoreAudioDevice::SetNominalSampleRate(Float64 sampleRate)
626 if (!m_DeviceId || sampleRate == 0.0f)
629 Float64 currentRate = GetNominalSampleRate();
630 if (currentRate == sampleRate)
631 return true; //No need to change
633 AudioObjectPropertyAddress propertyAddress;
634 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
635 propertyAddress.mElement = 0;
636 propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
638 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float64), &sampleRate);
641 CLog::Log(LOGERROR, "CCoreAudioDevice::SetNominalSampleRate: "
642 "Unable to set current device sample rate to %0.0f. Error = %s",
643 (float)sampleRate, GetError(ret).c_str());
646 if (m_SampleRateRestore == 0.0f)
647 m_SampleRateRestore = currentRate;
652 UInt32 CCoreAudioDevice::GetNumLatencyFrames()
654 UInt32 num_latency_frames = 0;
658 // number of frames of latency in the AudioDevice
659 AudioObjectPropertyAddress propertyAddress;
660 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
661 propertyAddress.mElement = 0;
662 propertyAddress.mSelector = kAudioDevicePropertyLatency;
665 UInt32 i_param_size = sizeof(uint32_t);
666 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
668 num_latency_frames += i_param;
670 // number of frames in the IO buffers
671 propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
672 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
674 num_latency_frames += i_param;
676 // number for frames in ahead the current hardware position that is safe to do IO
677 propertyAddress.mSelector = kAudioDevicePropertySafetyOffset;
678 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
680 num_latency_frames += i_param;
682 return (num_latency_frames);
685 UInt32 CCoreAudioDevice::GetBufferSize()
690 AudioObjectPropertyAddress propertyAddress;
691 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
692 propertyAddress.mElement = 0;
693 propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
696 UInt32 propertySize = sizeof(size);
697 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &size);
699 CLog::Log(LOGERROR, "CCoreAudioDevice::GetBufferSize: "
700 "Unable to retrieve buffer size. Error = %s", GetError(ret).c_str());
704 bool CCoreAudioDevice::SetBufferSize(UInt32 size)
709 AudioObjectPropertyAddress propertyAddress;
710 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
711 propertyAddress.mElement = 0;
712 propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
714 UInt32 propertySize = sizeof(size);
715 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, propertySize, &size);
718 CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: "
719 "Unable to set buffer size. Error = %s", GetError(ret).c_str());
722 if (GetBufferSize() != size)
723 CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: Buffer size change not applied.");
725 return (ret == noErr);