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 "CoreAudioAEHAL.h"
23 #include "CoreAudioChannelLayout.h"
24 #include "CoreAudioHardware.h"
25 #include "utils/log.h"
27 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
29 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
30 CCoreAudioDevice::CCoreAudioDevice() :
36 m_ObjectListenerProc (NULL ),
37 m_SampleRateRestore (0.0f ),
40 m_OutputBufferIndex (0 ),
41 m_BufferSizeRestore (0 )
45 CCoreAudioDevice::CCoreAudioDevice(AudioDeviceID deviceId) :
48 m_DeviceId (deviceId ),
51 m_ObjectListenerProc (NULL ),
52 m_SampleRateRestore (0.0f ),
55 m_OutputBufferIndex (0 ),
56 m_BufferSizeRestore (0 )
60 CCoreAudioDevice::~CCoreAudioDevice()
65 bool CCoreAudioDevice::Open(AudioDeviceID deviceId)
67 m_DeviceId = deviceId;
68 m_BufferSizeRestore = GetBufferSize();
72 void CCoreAudioDevice::Close()
77 // Stop the device if it was started
80 // Unregister the IOProc if we have one
82 SetInputSource(NULL, 0, 0);
85 CCoreAudioHardware::SetAutoHogMode(false);
87 if (m_MixerRestore > -1) // We changed the mixer status
88 SetMixingSupport((m_MixerRestore ? true : false));
91 if (m_SampleRateRestore != 0.0f)
92 SetNominalSampleRate(m_SampleRateRestore);
94 if (m_BufferSizeRestore && m_BufferSizeRestore != GetBufferSize())
96 SetBufferSize(m_BufferSizeRestore);
97 m_BufferSizeRestore = 0;
103 m_ObjectListenerProc = NULL;
106 void CCoreAudioDevice::Start()
108 if (!m_DeviceId || m_Started)
111 OSStatus ret = AudioDeviceStart(m_DeviceId, m_IoProc);
113 CLog::Log(LOGERROR, "CCoreAudioDevice::Start: "
114 "Unable to start device. Error = %s", GetError(ret).c_str());
119 void CCoreAudioDevice::Stop()
121 if (!m_DeviceId || !m_Started)
124 OSStatus ret = AudioDeviceStop(m_DeviceId, m_IoProc);
126 CLog::Log(LOGERROR, "CCoreAudioDevice::Stop: "
127 "Unable to stop device. Error = %s", GetError(ret).c_str());
131 void CCoreAudioDevice::RemoveObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
136 AudioObjectPropertyAddress audioProperty;
137 audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
138 audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
139 audioProperty.mElement = kAudioObjectPropertyElementWildcard;
141 OSStatus ret = AudioObjectRemovePropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
144 CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveObjectListenerProc: "
145 "Unable to set ObjectListener callback. Error = %s", GetError(ret).c_str());
147 m_ObjectListenerProc = NULL;
150 bool CCoreAudioDevice::SetObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
152 // Allow only one ObjectListener at a time
153 if (!m_DeviceId || m_ObjectListenerProc)
156 AudioObjectPropertyAddress audioProperty;
157 audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
158 audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
159 audioProperty.mElement = kAudioObjectPropertyElementWildcard;
161 OSStatus ret = AudioObjectAddPropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
165 CLog::Log(LOGERROR, "CCoreAudioDevice::SetObjectListenerProc: "
166 "Unable to remove ObjectListener callback. Error = %s", GetError(ret).c_str());
170 m_ObjectListenerProc = callback;
174 bool CCoreAudioDevice::SetInputSource(ICoreAudioSource* pSource, unsigned int frameSize, unsigned int outputBufferIndex)
177 m_frameSize = frameSize;
178 m_OutputBufferIndex = outputBufferIndex;
183 return RemoveIOProc();
186 bool CCoreAudioDevice::AddIOProc()
188 // Allow only one IOProc at a time
189 if (!m_DeviceId || m_IoProc)
192 OSStatus ret = AudioDeviceCreateIOProcID(m_DeviceId, DirectRenderCallback, this, &m_IoProc);
195 CLog::Log(LOGERROR, "CCoreAudioDevice::AddIOProc: "
196 "Unable to add IOProc. Error = %s", GetError(ret).c_str());
206 bool CCoreAudioDevice::RemoveIOProc()
208 if (!m_DeviceId || !m_IoProc)
213 OSStatus ret = AudioDeviceDestroyIOProcID(m_DeviceId, m_IoProc);
215 CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveIOProc: "
216 "Unable to remove IOProc. Error = %s", GetError(ret).c_str());
218 m_IoProc = NULL; // Clear the reference no matter what
226 std::string CCoreAudioDevice::GetName()
231 AudioObjectPropertyAddress propertyAddress;
232 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
233 propertyAddress.mElement = 0;
234 propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
237 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
241 std::string name = "";
242 char *buff = new char[propertySize + 1];
243 buff[propertySize] = 0x00;
244 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, buff);
247 CLog::Log(LOGERROR, "CCoreAudioDevice::GetName: "
248 "Unable to get device name - id: 0x%04x. Error = %s", (uint)m_DeviceId, GetError(ret).c_str());
260 UInt32 CCoreAudioDevice::GetTotalOutputChannels()
267 AudioObjectPropertyAddress propertyAddress;
268 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
269 propertyAddress.mElement = 0;
270 propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
273 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &size);
277 AudioBufferList* pList = (AudioBufferList*)malloc(size);
278 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, pList);
281 for(UInt32 buffer = 0; buffer < pList->mNumberBuffers; ++buffer)
282 channels += pList->mBuffers[buffer].mNumberChannels;
286 CLog::Log(LOGERROR, "CCoreAudioDevice::GetTotalOutputChannels: "
287 "Unable to get total device output channels - id: 0x%04x. Error = %s",
288 (uint)m_DeviceId, GetError(ret).c_str());
296 bool CCoreAudioDevice::GetStreams(AudioStreamIdList* pList)
298 if (!pList || !m_DeviceId)
301 AudioObjectPropertyAddress propertyAddress;
302 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
303 propertyAddress.mElement = 0;
304 propertyAddress.mSelector = kAudioDevicePropertyStreams;
306 UInt32 propertySize = 0;
307 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
311 UInt32 streamCount = propertySize / sizeof(AudioStreamID);
312 AudioStreamID* pStreamList = new AudioStreamID[streamCount];
313 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pStreamList);
316 for (UInt32 stream = 0; stream < streamCount; stream++)
317 pList->push_back(pStreamList[stream]);
319 delete[] pStreamList;
325 bool CCoreAudioDevice::IsRunning()
327 AudioObjectPropertyAddress propertyAddress;
328 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
329 propertyAddress.mElement = 0;
330 propertyAddress.mSelector = kAudioDevicePropertyDeviceIsRunning;
332 UInt32 isRunning = 0;
333 UInt32 propertySize = sizeof(isRunning);
334 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &isRunning);
338 return isRunning != 0;
341 bool CCoreAudioDevice::SetHogStatus(bool hog)
343 // According to Jeff Moore (Core Audio, Apple), Setting kAudioDevicePropertyHogMode
344 // is a toggle and the only way to tell if you do get hog mode is to compare
345 // the returned pid against getpid, if the match, you have hog mode, if not you don't.
349 AudioObjectPropertyAddress propertyAddress;
350 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
351 propertyAddress.mElement = 0;
352 propertyAddress.mSelector = kAudioDevicePropertyHogMode;
359 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(m_HogPid), &m_HogPid);
361 // even if setting hogmode was successfull our PID might not get written
362 // into m_HogPid (so it stays -1). Readback hogstatus for judging if we
363 // had success on getting hog status
364 // We do this only when AudioObjectSetPropertyData didn't set m_HogPid because
365 // it seems that in the other cases the GetHogStatus could return -1
366 // which would overwrite our valid m_HogPid again
367 // Man we should never touch this shit again ;)
369 m_HogPid = GetHogStatus();
371 if (ret || m_HogPid != getpid())
373 CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
374 "Unable to set 'hog' status. Error = %s", GetError(ret).c_str());
385 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(hogPid), &hogPid);
386 if (ret || hogPid == getpid())
388 CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
389 "Unable to release 'hog' status. Error = %s", GetError(ret).c_str());
392 // Reset internal state
399 pid_t CCoreAudioDevice::GetHogStatus()
404 AudioObjectPropertyAddress propertyAddress;
405 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
406 propertyAddress.mElement = 0;
407 propertyAddress.mSelector = kAudioDevicePropertyHogMode;
410 UInt32 size = sizeof(hogPid);
411 AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &hogPid);
416 bool CCoreAudioDevice::SetMixingSupport(UInt32 mix)
421 if (!GetMixingSupport())
425 if (m_MixerRestore == -1)
427 // This is our first change to this setting. Store the original setting for restore
428 restore = (GetMixingSupport() ? 1 : 0);
431 AudioObjectPropertyAddress propertyAddress;
432 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
433 propertyAddress.mElement = 0;
434 propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
436 UInt32 mixEnable = mix ? 1 : 0;
437 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(mixEnable), &mixEnable);
440 CLog::Log(LOGERROR, "CCoreAudioDevice::SetMixingSupport: "
441 "Unable to set MixingSupport to %s. Error = %s", mix ? "'On'" : "'Off'", GetError(ret).c_str());
444 if (m_MixerRestore == -1)
445 m_MixerRestore = restore;
449 bool CCoreAudioDevice::GetMixingSupport()
456 Boolean writable = false;
458 AudioObjectPropertyAddress propertyAddress;
459 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
460 propertyAddress.mElement = 0;
461 propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
463 if( AudioObjectHasProperty( m_DeviceId, &propertyAddress ) )
465 OSStatus ret = AudioObjectIsPropertySettable(m_DeviceId, &propertyAddress, &writable);
468 CLog::Log(LOGERROR, "CCoreAudioDevice::SupportsMixing: "
469 "Unable to get propertyinfo mixing support. Error = %s", GetError(ret).c_str());
476 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &mix);
481 CLog::Log(LOGERROR, "CCoreAudioDevice::SupportsMixing: "
482 "Device mixing support : %s.", mix ? "'Yes'" : "'No'");
487 bool CCoreAudioDevice::SetCurrentVolume(Float32 vol)
492 AudioObjectPropertyAddress propertyAddress;
493 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
494 propertyAddress.mElement = 0;
495 propertyAddress.mSelector = kHALOutputParam_Volume;
497 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float32), &vol);
500 CLog::Log(LOGERROR, "CCoreAudioDevice::SetCurrentVolume: "
501 "Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
507 bool CCoreAudioDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
512 AudioObjectPropertyAddress propertyAddress;
513 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
514 propertyAddress.mElement = 0;
515 propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
517 UInt32 propertySize = 0;
518 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
522 void* pBuf = malloc(propertySize);
523 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf);
525 CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: "
526 "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
529 // Copy the result into the caller's instance
530 layout.CopyLayout(*((AudioChannelLayout*)pBuf));
533 return (ret == noErr);
536 bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList)
538 if (!pList || !m_DeviceId)
541 AudioObjectPropertyAddress propertyAddress;
542 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
543 propertyAddress.mElement = 0;
544 propertyAddress.mSelector = kAudioDevicePropertyDataSources;
546 UInt32 propertySize = 0;
547 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
551 UInt32 sources = propertySize / sizeof(UInt32);
552 UInt32* pSources = new UInt32[sources];
553 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pSources);
556 for (UInt32 i = 0; i < sources; i++)
557 pList->push_back(pSources[i]);
563 Float64 CCoreAudioDevice::GetNominalSampleRate()
568 AudioObjectPropertyAddress propertyAddress;
569 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
570 propertyAddress.mElement = 0;
571 propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
573 Float64 sampleRate = 0.0f;
574 UInt32 propertySize = sizeof(Float64);
575 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &sampleRate);
578 CLog::Log(LOGERROR, "CCoreAudioDevice::GetNominalSampleRate: "
579 "Unable to retrieve current device sample rate. Error = %s", GetError(ret).c_str());
585 bool CCoreAudioDevice::SetNominalSampleRate(Float64 sampleRate)
587 if (!m_DeviceId || sampleRate == 0.0f)
590 Float64 currentRate = GetNominalSampleRate();
591 if (currentRate == sampleRate)
592 return true; //No need to change
594 AudioObjectPropertyAddress propertyAddress;
595 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
596 propertyAddress.mElement = 0;
597 propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
599 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float64), &sampleRate);
602 CLog::Log(LOGERROR, "CCoreAudioDevice::SetNominalSampleRate: "
603 "Unable to set current device sample rate to %0.0f. Error = %s",
604 (float)sampleRate, GetError(ret).c_str());
607 if (m_SampleRateRestore == 0.0f)
608 m_SampleRateRestore = currentRate;
613 UInt32 CCoreAudioDevice::GetNumLatencyFrames()
615 UInt32 num_latency_frames = 0;
619 // number of frames of latency in the AudioDevice
620 AudioObjectPropertyAddress propertyAddress;
621 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
622 propertyAddress.mElement = 0;
623 propertyAddress.mSelector = kAudioDevicePropertyLatency;
626 UInt32 i_param_size = sizeof(uint32_t);
627 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
629 num_latency_frames += i_param;
631 // number of frames in the IO buffers
632 propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
633 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
635 num_latency_frames += i_param;
637 // number for frames in ahead the current hardware position that is safe to do IO
638 propertyAddress.mSelector = kAudioDevicePropertySafetyOffset;
639 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
641 num_latency_frames += i_param;
643 return (num_latency_frames);
646 UInt32 CCoreAudioDevice::GetBufferSize()
651 AudioObjectPropertyAddress propertyAddress;
652 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
653 propertyAddress.mElement = 0;
654 propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
657 UInt32 propertySize = sizeof(size);
658 OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &size);
660 CLog::Log(LOGERROR, "CCoreAudioDevice::GetBufferSize: "
661 "Unable to retrieve buffer size. Error = %s", GetError(ret).c_str());
665 bool CCoreAudioDevice::SetBufferSize(UInt32 size)
670 AudioObjectPropertyAddress propertyAddress;
671 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
672 propertyAddress.mElement = 0;
673 propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
675 UInt32 propertySize = sizeof(size);
676 OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, propertySize, &size);
679 CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: "
680 "Unable to set buffer size. Error = %s", GetError(ret).c_str());
683 if (GetBufferSize() != size)
684 CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: Buffer size change not applied.");
686 return (ret == noErr);
689 OSStatus CCoreAudioDevice::DirectRenderCallback(AudioDeviceID inDevice,
690 const AudioTimeStamp *inNow,
691 const AudioBufferList *inInputData,
692 const AudioTimeStamp *inInputTime,
693 AudioBufferList *outOutputData,
694 const AudioTimeStamp *inOutputTime,
697 OSStatus ret = noErr;
698 CCoreAudioDevice *audioDevice = (CCoreAudioDevice*)inClientData;
700 if (audioDevice->m_pSource && audioDevice->m_frameSize)
702 UInt32 frames = outOutputData->mBuffers[audioDevice->m_OutputBufferIndex].mDataByteSize / audioDevice->m_frameSize;
703 ret = audioDevice->m_pSource->Render(NULL, inInputTime, 0, frames, outOutputData);
707 outOutputData->mBuffers[audioDevice->m_OutputBufferIndex].mDataByteSize = 0;