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 "CoreAudioStream.h"
23 #include "CoreAudioAEHAL.h"
24 #include "utils/log.h"
25 #include "utils/StdString.h"
27 CCoreAudioStream::CCoreAudioStream() :
30 m_OriginalVirtualFormat.mFormatID = 0;
31 m_OriginalPhysicalFormat.mFormatID = 0;
34 CCoreAudioStream::~CCoreAudioStream()
39 bool CCoreAudioStream::Open(AudioStreamID streamId)
41 m_StreamId = streamId;
42 CLog::Log(LOGDEBUG, "CCoreAudioStream::Open: Opened stream 0x%04x.", (uint)m_StreamId);
44 // watch for physical property changes.
45 AudioObjectPropertyAddress propertyAOPA;
46 propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
47 propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
48 propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
49 if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
50 CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a physical property listener.");
52 // watch for virtual property changes.
53 propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
54 propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
55 propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
56 if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
57 CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a virtual property listener.");
62 // TODO: Should it even be possible to change both the
63 // physical and virtual formats, since the devices do it themselves?
64 void CCoreAudioStream::Close()
69 std::string formatString;
71 // remove the physical/virtual property listeners before we make changes
72 // that will trigger callbacks that we do not care about.
73 AudioObjectPropertyAddress propertyAOPA;
74 propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
75 propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
76 propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
77 if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
78 CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
80 propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
81 propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
82 propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
83 if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
84 CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
86 // Revert any format changes we made
87 if (m_OriginalVirtualFormat.mFormatID && m_StreamId)
89 CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
90 "Restoring original virtual format for stream 0x%04x. (%s)",
91 (uint)m_StreamId, StreamDescriptionToString(m_OriginalVirtualFormat, formatString));
92 AudioStreamBasicDescription setFormat = m_OriginalVirtualFormat;
93 SetVirtualFormat(&setFormat);
95 if (m_OriginalPhysicalFormat.mFormatID && m_StreamId)
97 CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
98 "Restoring original physical format for stream 0x%04x. (%s)",
99 (uint)m_StreamId, StreamDescriptionToString(m_OriginalPhysicalFormat, formatString));
100 AudioStreamBasicDescription setFormat = m_OriginalPhysicalFormat;
101 SetPhysicalFormat(&setFormat);
104 m_OriginalVirtualFormat.mFormatID = 0;
105 m_OriginalPhysicalFormat.mFormatID = 0;
106 CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Closed stream 0x%04x.", (uint)m_StreamId);
110 UInt32 CCoreAudioStream::GetDirection()
116 UInt32 size = sizeof(UInt32);
118 AudioObjectPropertyAddress propertyAddress;
119 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
120 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
121 propertyAddress.mSelector = kAudioStreamPropertyDirection;
123 OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, &val);
130 UInt32 CCoreAudioStream::GetTerminalType()
136 UInt32 size = sizeof(UInt32);
138 AudioObjectPropertyAddress propertyAddress;
139 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
140 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
141 propertyAddress.mSelector = kAudioStreamPropertyTerminalType;
143 OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, &val);
149 UInt32 CCoreAudioStream::GetNumLatencyFrames()
154 UInt32 i_param_size = sizeof(uint32_t);
155 UInt32 i_param, num_latency_frames = 0;
157 AudioObjectPropertyAddress propertyAddress;
158 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
159 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
160 propertyAddress.mSelector = kAudioStreamPropertyLatency;
162 // number of frames of latency in the AudioStream
163 OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
166 num_latency_frames += i_param;
169 return num_latency_frames;
172 bool CCoreAudioStream::GetVirtualFormat(AudioStreamBasicDescription* pDesc)
174 if (!pDesc || !m_StreamId)
177 UInt32 size = sizeof(AudioStreamBasicDescription);
179 AudioObjectPropertyAddress propertyAddress;
180 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
181 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
182 propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
183 OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &size);
187 ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, pDesc);
193 bool CCoreAudioStream::SetVirtualFormat(AudioStreamBasicDescription* pDesc)
195 if (!pDesc || !m_StreamId)
198 std::string formatString;
200 if (!m_OriginalVirtualFormat.mFormatID)
202 // Store the original format (as we found it) so that it can be restored later
203 if (!GetVirtualFormat(&m_OriginalVirtualFormat))
205 CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
206 "Unable to retrieve current virtual format for stream 0x%04x.", (uint)m_StreamId);
210 m_virtual_format_event.Reset();
212 AudioObjectPropertyAddress propertyAddress;
213 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
214 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
215 propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
217 UInt32 propertySize = sizeof(AudioStreamBasicDescription);
218 OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc);
221 CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
222 "Unable to set virtual format for stream 0x%04x. Error = %s",
223 (uint)m_StreamId, GetError(ret).c_str());
227 // The AudioStreamSetProperty is not only asynchronious,
228 // it is also not Atomic, in its behaviour.
229 // Therefore we check 5 times before we really give up.
230 // FIXME: failing isn't actually implemented yet.
231 for (int i = 0; i < 10; ++i)
233 AudioStreamBasicDescription checkVirtualFormat;
234 if (!GetVirtualFormat(&checkVirtualFormat))
236 CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
237 "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
240 if (checkVirtualFormat.mSampleRate == pDesc->mSampleRate &&
241 checkVirtualFormat.mFormatID == pDesc->mFormatID &&
242 checkVirtualFormat.mFramesPerPacket == pDesc->mFramesPerPacket)
244 // The right format is now active.
245 CLog::Log(LOGDEBUG, "CCoreAudioStream::SetVirtualFormat: "
246 "Virtual format for stream 0x%04x. now active (%s)",
247 (uint)m_StreamId, StreamDescriptionToString(checkVirtualFormat, formatString));
250 m_virtual_format_event.WaitMSec(100);
255 bool CCoreAudioStream::GetPhysicalFormat(AudioStreamBasicDescription* pDesc)
257 if (!pDesc || !m_StreamId)
260 UInt32 size = sizeof(AudioStreamBasicDescription);
262 AudioObjectPropertyAddress propertyAddress;
263 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
264 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
265 propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
267 OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, pDesc);
273 bool CCoreAudioStream::SetPhysicalFormat(AudioStreamBasicDescription* pDesc)
275 if (!pDesc || !m_StreamId)
278 std::string formatString;
280 if (!m_OriginalPhysicalFormat.mFormatID)
282 // Store the original format (as we found it) so that it can be restored later
283 if (!GetPhysicalFormat(&m_OriginalPhysicalFormat))
285 CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
286 "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
290 m_physical_format_event.Reset();
292 AudioObjectPropertyAddress propertyAddress;
293 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
294 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
295 propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
297 UInt32 propertySize = sizeof(AudioStreamBasicDescription);
298 OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc);
301 CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
302 "Unable to set physical format for stream 0x%04x. Error = %s",
303 (uint)m_StreamId, GetError(ret).c_str());
307 // The AudioStreamSetProperty is not only asynchronious,
308 // it is also not Atomic, in its behaviour.
309 // Therefore we check 5 times before we really give up.
310 // FIXME: failing isn't actually implemented yet.
311 for(int i = 0; i < 10; ++i)
313 AudioStreamBasicDescription checkPhysicalFormat;
314 if (!GetPhysicalFormat(&checkPhysicalFormat))
316 CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
317 "Unable to retrieve current physical format for stream 0x%04x.", (uint)m_StreamId);
320 if (checkPhysicalFormat.mSampleRate == pDesc->mSampleRate &&
321 checkPhysicalFormat.mFormatID == pDesc->mFormatID &&
322 checkPhysicalFormat.mFramesPerPacket == pDesc->mFramesPerPacket)
324 // The right format is now active.
325 CLog::Log(LOGDEBUG, "CCoreAudioStream::SetPhysicalFormat: "
326 "Physical format for stream 0x%04x. now active (%s)",
327 (uint)m_StreamId, StreamDescriptionToString(checkPhysicalFormat, formatString));
330 m_physical_format_event.WaitMSec(100);
336 bool CCoreAudioStream::GetAvailableVirtualFormats(StreamFormatList* pList)
338 if (!pList || !m_StreamId)
341 AudioObjectPropertyAddress propertyAddress;
342 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
343 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
344 propertyAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
346 UInt32 propertySize = 0;
347 OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &propertySize);
351 UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
352 AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
353 ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &propertySize, pFormatList);
356 for (UInt32 format = 0; format < formatCount; format++)
357 pList->push_back(pFormatList[format]);
359 delete[] pFormatList;
360 return (ret == noErr);
363 bool CCoreAudioStream::GetAvailablePhysicalFormats(StreamFormatList* pList)
365 if (!pList || !m_StreamId)
368 AudioObjectPropertyAddress propertyAddress;
369 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
370 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
371 propertyAddress.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
373 UInt32 propertySize = 0;
374 OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &propertySize);
378 UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
379 AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
380 ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &propertySize, pFormatList);
383 for (UInt32 format = 0; format < formatCount; format++)
384 pList->push_back(pFormatList[format]);
386 delete[] pFormatList;
387 return (ret == noErr);
390 OSStatus CCoreAudioStream::HardwareStreamListener(AudioObjectID inObjectID,
391 UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
393 CCoreAudioStream *ca_stream = (CCoreAudioStream*)inClientData;
395 for (UInt32 i = 0; i < inNumberAddresses; i++)
397 if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat)
399 AudioStreamBasicDescription actualFormat;
400 UInt32 propertySize = sizeof(AudioStreamBasicDescription);
401 // hardware physical format has changed.
402 if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
404 CStdString formatString;
405 CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
406 "Hardware physical format changed to %s", StreamDescriptionToString(actualFormat, formatString));
407 ca_stream->m_physical_format_event.Set();
410 else if (inAddresses[i].mSelector == kAudioStreamPropertyVirtualFormat)
412 // hardware virtual format has changed.
413 AudioStreamBasicDescription actualFormat;
414 UInt32 propertySize = sizeof(AudioStreamBasicDescription);
415 if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
417 CStdString formatString;
418 CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
419 "Hardware virtual format changed to %s", StreamDescriptionToString(actualFormat, formatString));
420 ca_stream->m_virtual_format_event.Set();