2 * Copyright (C) 2005-2014 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 "cores/AudioEngine/AEFactory.h"
22 #include "cores/AudioEngine/Sinks/AESinkDARWINOSX.h"
23 #include "cores/AudioEngine/Utils/AEUtil.h"
24 #include "cores/AudioEngine/Utils/AERingBuffer.h"
25 #include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
26 #include "cores/AudioEngine/Sinks/osx/CoreAudioHardware.h"
27 #include "cores/AudioEngine/Sinks/osx/CoreAudioChannelLayout.h"
28 #include "osx/DarwinUtils.h"
29 #include "utils/log.h"
30 #include "utils/StringUtils.h"
31 #include "threads/Condition.h"
32 #include "threads/CriticalSection.h"
36 #define CA_MAX_CHANNELS 16
37 // default channel map - in case it can't be fetched from the device
38 static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = {
39 AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
51 // map coraudio channel labels to activeae channel labels
52 static enum AEChannel CAChannelToAEChannel(AudioChannelLabel CAChannelLabel)
54 enum AEChannel ret = AE_CH_NULL;
55 static unsigned int unknownChannel = AE_CH_UNKNOWN1;
56 switch(CAChannelLabel)
58 case kAudioChannelLabel_Left:
61 case kAudioChannelLabel_Right:
64 case kAudioChannelLabel_Center:
67 case kAudioChannelLabel_LFEScreen:
70 case kAudioChannelLabel_LeftSurroundDirect:
73 case kAudioChannelLabel_RightSurroundDirect:
76 case kAudioChannelLabel_LeftCenter:
79 case kAudioChannelLabel_RightCenter:
82 case kAudioChannelLabel_CenterSurround:
85 case kAudioChannelLabel_LeftSurround:
88 case kAudioChannelLabel_RightSurround:
91 case kAudioChannelLabel_VerticalHeightLeft:
94 case kAudioChannelLabel_VerticalHeightRight:
97 case kAudioChannelLabel_VerticalHeightCenter:
100 case kAudioChannelLabel_TopCenterSurround:
103 case kAudioChannelLabel_TopBackLeft:
106 case kAudioChannelLabel_TopBackRight:
109 case kAudioChannelLabel_TopBackCenter:
112 case kAudioChannelLabel_RearSurroundLeft:
115 case kAudioChannelLabel_RearSurroundRight:
118 case kAudioChannelLabel_LeftWide:
121 case kAudioChannelLabel_RightWide:
124 case kAudioChannelLabel_LFE2:
127 case kAudioChannelLabel_LeftTotal:
130 case kAudioChannelLabel_RightTotal:
133 case kAudioChannelLabel_HearingImpaired:
136 case kAudioChannelLabel_Narration:
139 case kAudioChannelLabel_Mono:
142 case kAudioChannelLabel_DialogCentricMix:
145 case kAudioChannelLabel_CenterSurroundDirect:
148 case kAudioChannelLabel_Haptic:
152 ret = (enum AEChannel)unknownChannel++;
154 if (unknownChannel > AE_CH_UNKNOWN8)
155 unknownChannel = AE_CH_UNKNOWN1;
160 //Note: in multichannel mode CA will either pull 2 channels of data (stereo) or 6/8 channels of data
161 //(every speaker setup with more then 2 speakers). The difference between the number of real speakers
162 //and 6/8 channels needs to be padded with unknown channels so that the sample size fits 6/8 channels
164 //device [in] - the device whose channel layout should be used
165 //channelMap [in/out] - if filled it will it indicates that we are called from initialize and we log the requested map, out returns the channelMap for device
166 //channelsPerFrame [in] - the number of channels this device is configured to (e.x. 2 or 6/8)
167 static void GetAEChannelMap(CCoreAudioDevice &device, CAEChannelInfo &channelMap, unsigned int channelsPerFrame)
169 CCoreAudioChannelLayout calayout;
170 bool logMapping = channelMap.Count() > 0; // only log if the engine requests a layout during init
171 bool mapAvailable = false;
172 unsigned int numberChannelsInDeviceLayout = CA_MAX_CHANNELS; // default 8 channels from CAChannelMap
173 AudioChannelLayout *layout = NULL;
175 // try to fetch either the multichannel or the stereo channel layout from the device
176 if (channelsPerFrame == 2 || channelMap.Count() == 2)
177 mapAvailable = device.GetPreferredChannelLayoutForStereo(calayout);
179 mapAvailable = device.GetPreferredChannelLayout(calayout);
181 // if a map was fetched - check if it is usable
185 if (layout == NULL || layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
186 mapAvailable = false;// wrong map format
188 numberChannelsInDeviceLayout = layout->mNumberChannelDescriptions;
191 // start the mapping action
192 // the number of channels to be added to the outgoing channelmap
193 // this is CA_MAX_CHANNELS at max and might be lower for some output devices (channelsPerFrame)
194 unsigned int numChannelsToMap = std::min((unsigned int)CA_MAX_CHANNELS, (unsigned int)channelsPerFrame);
196 // if there was a map fetched we force the number of
197 // channels to map to channelsPerFrame (this allows mapping
198 // of more then CA_MAX_CHANNELS if needed)
200 numChannelsToMap = channelsPerFrame;
202 std::string layoutStr;
206 CLog::Log(LOGDEBUG, "%s Engine requests layout %s", __FUNCTION__, ((std::string)channelMap).c_str());
209 CLog::Log(LOGDEBUG, "%s trying to map to %s layout: %s", __FUNCTION__, channelsPerFrame == 2 ? "stereo" : "multichannel", calayout.ChannelLayoutToString(*layout, layoutStr));
211 CLog::Log(LOGDEBUG, "%s no map available - using static multichannel map layout", __FUNCTION__);
214 channelMap.Reset();// start with an empty map
216 for (unsigned int channel = 0; channel < numChannelsToMap; channel++)
218 // we only try to map channels which are defined in the device layout
219 enum AEChannel currentChannel;
220 if (channel < numberChannelsInDeviceLayout)
222 // get the channel from the fetched map
224 currentChannel = CAChannelToAEChannel(layout->mChannelDescriptions[channel].mChannelLabel);
225 else// get the channel from the default map
226 currentChannel = CAChannelMap[channel];
229 else// fill with unknown channels
230 currentChannel = CAChannelToAEChannel(kAudioChannelLabel_Unknown);
232 if(!channelMap.HasChannel(currentChannel))// only add if not already added
233 channelMap += currentChannel;
237 CLog::Log(LOGDEBUG, "%s mapped channels to layout %s", __FUNCTION__, ((std::string)channelMap).c_str());
240 static bool HasSampleRate(const AESampleRateList &list, const unsigned int samplerate)
242 for (size_t i = 0; i < list.size(); ++i)
244 if (list[i] == samplerate)
250 static bool HasDataFormat(const AEDataFormatList &list, const enum AEDataFormat format)
252 for (size_t i = 0; i < list.size(); ++i)
254 if (list[i] == format)
260 typedef std::vector< std::pair<AudioDeviceID, CAEDeviceInfo> > CADeviceList;
262 static void EnumerateDevices(CADeviceList &list)
264 CAEDeviceInfo device;
266 std::string defaultDeviceName;
267 CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
269 CoreAudioDeviceList deviceIDList;
270 CCoreAudioHardware::GetOutputDevices(&deviceIDList);
271 while (!deviceIDList.empty())
273 AudioDeviceID deviceID = deviceIDList.front();
274 CCoreAudioDevice caDevice(deviceID);
276 device.m_channels.Reset();
277 device.m_dataFormats.clear();
278 device.m_sampleRates.clear();
280 device.m_deviceType = AE_DEVTYPE_PCM;
281 device.m_deviceName = caDevice.GetName();
282 device.m_displayName = device.m_deviceName;
283 device.m_displayNameExtra = "";
285 // flag indicating that passthroughformats where added throughout the stream enumeration
286 bool hasPassthroughFormats = false;
287 // the maximum number of channels found in the streams
288 UInt32 numMaxChannels = 0;
289 // the terminal type as reported by ca
290 UInt32 caTerminalType = 0;
292 bool isDigital = caDevice.IsDigital(caTerminalType);
295 CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
296 AudioStreamIdList streams;
297 if (caDevice.GetStreams(&streams))
299 for (AudioStreamIdList::iterator j = streams.begin(); j != streams.end(); ++j)
301 StreamFormatList streamFormats;
302 if (CCoreAudioStream::GetAvailablePhysicalFormats(*j, &streamFormats))
304 for (StreamFormatList::iterator i = streamFormats.begin(); i != streamFormats.end(); ++i)
306 AudioStreamBasicDescription desc = i->mFormat;
307 std::string formatString;
308 CLog::Log(LOGDEBUG, "EnumerateDevices:Format(%s)" ,
309 StreamDescriptionToString(desc, formatString));
311 // add stream format info
312 switch (desc.mFormatID)
314 case kAudioFormatAC3:
315 case kAudioFormat60958AC3:
316 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
317 device.m_dataFormats.push_back(AE_FMT_AC3);
318 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
319 device.m_dataFormats.push_back(AE_FMT_DTS);
320 hasPassthroughFormats = true;
321 isDigital = true;// sanity - those are always digital devices!
324 AEDataFormat format = AE_FMT_INVALID;
325 switch(desc.mBitsPerChannel)
328 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
329 format = AE_FMT_S16BE;
332 // if it is no digital stream per definition
333 // check if the device name suggests that it is digital
334 // (some hackintonshs are not so smart in announcing correct
338 std::string devNameLower = device.m_deviceName;
339 StringUtils::ToLower(devNameLower);
340 isDigital = devNameLower.find("digital") != std::string::npos;
343 /* Passthrough is possible with a 2ch digital output */
344 if (desc.mChannelsPerFrame == 2 && isDigital)
346 if (desc.mSampleRate == 48000)
348 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
349 device.m_dataFormats.push_back(AE_FMT_AC3);
350 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
351 device.m_dataFormats.push_back(AE_FMT_DTS);
352 hasPassthroughFormats = true;
354 else if (desc.mSampleRate == 192000)
356 if (!HasDataFormat(device.m_dataFormats, AE_FMT_EAC3))
357 device.m_dataFormats.push_back(AE_FMT_EAC3);
358 hasPassthroughFormats = true;
361 format = AE_FMT_S16LE;
365 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
366 format = AE_FMT_S24BE3;
368 format = AE_FMT_S24LE3;
371 if (desc.mFormatFlags & kAudioFormatFlagIsFloat)
372 format = AE_FMT_FLOAT;
375 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
376 format = AE_FMT_S32BE;
378 format = AE_FMT_S32LE;
383 if (numMaxChannels < desc.mChannelsPerFrame)
384 numMaxChannels = desc.mChannelsPerFrame;
386 if (format != AE_FMT_INVALID && !HasDataFormat(device.m_dataFormats, format))
387 device.m_dataFormats.push_back(format);
391 // add sample rate info
392 // for devices which return kAudioStreamAnyRatee
393 // we add 44.1khz and 48khz - user can use
394 // the "fixed" audio config to force one of them
395 if (desc.mSampleRate == kAudioStreamAnyRate)
397 CLog::Log(LOGINFO, "%s reported samplerate is kAudioStreamAnyRate adding 44.1khz and 48khz", __FUNCTION__);
398 desc.mSampleRate = 44100;
399 if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
400 device.m_sampleRates.push_back(desc.mSampleRate);
401 desc.mSampleRate = 48000;
404 if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
405 device.m_sampleRates.push_back(desc.mSampleRate);
412 // flag indicating that the device name "sounds" like HDMI
413 bool hasHdmiName = device.m_deviceName.find("HDMI") != std::string::npos;
414 // flag indicating that the device name "sounds" like DisplayPort
415 bool hasDisplayPortName = device.m_deviceName.find("DisplayPort") != std::string::npos;
417 // decide the type of the device based on the discovered information
419 // device defaults to PCM (see start of the while loop)
420 // it can be HDMI, DisplayPort or Optical
421 // for all of those types it needs to support
422 // passthroughformats and needs to be a digital port
423 if (hasPassthroughFormats && isDigital)
425 // if the max number of channels was more then 2
426 // this can be HDMI or DisplayPort or Thunderbolt
427 if (numMaxChannels > 2)
429 // either the devicename suggests its HDMI
430 // or CA reported the terminalType as HDMI
431 if (hasHdmiName || caTerminalType == kIOAudioDeviceTransportTypeHdmi)
432 device.m_deviceType = AE_DEVTYPE_HDMI;
434 // either the devicename suggests its DisplayPort
435 // or CA reported the terminalType as DisplayPort or Thunderbolt
436 if (hasDisplayPortName || caTerminalType == kIOAudioDeviceTransportTypeDisplayPort || caTerminalType == kIOAudioDeviceTransportTypeThunderbolt)
437 device.m_deviceType = AE_DEVTYPE_DP;
439 else// treat all other digital passthrough devices as optical
440 device.m_deviceType = AE_DEVTYPE_IEC958;
443 // devicename based overwrites from former code - maybe FIXME at some point when we
444 // are sure that the upper detection does its job in all[tm] use cases
446 device.m_deviceType = AE_DEVTYPE_HDMI;
447 if (hasDisplayPortName)
448 device.m_deviceType = AE_DEVTYPE_DP;
450 //get channel map to match the devices channel layout as set in audio-midi-setup
451 GetAEChannelMap(caDevice, device.m_channels, caDevice.GetTotalOutputChannels());
453 list.push_back(std::make_pair(deviceID, device));
454 //in the first place of the list add the default device
455 //with name "default" - if this is selected
456 //we will output to whatever osx claims to be default
457 //(allows transition from headphones to speaker and stuff
459 if(defaultDeviceName == device.m_deviceName)
461 device.m_deviceName = "default";
462 device.m_displayName = "Default";
463 list.insert(list.begin(), std::make_pair(deviceID, device));
466 deviceIDList.pop_front();
470 /* static, threadsafe access to the device list */
471 static CADeviceList s_devices;
472 static CCriticalSection s_devicesLock;
474 static void EnumerateDevices()
476 CADeviceList devices;
477 EnumerateDevices(devices);
479 CSingleLock lock(s_devicesLock);
484 static CADeviceList GetDevices()
488 CSingleLock lock(s_devicesLock);
494 OSStatus deviceChangedCB(AudioObjectID inObjectID,
495 UInt32 inNumberAddresses,
496 const AudioObjectPropertyAddress inAddresses[],
499 bool deviceChanged = false;
500 static AudioDeviceID oldDefaultDevice = 0;
501 AudioDeviceID currentDefaultOutputDevice = 0;
503 for (unsigned int i = 0; i < inNumberAddresses; i++)
505 switch (inAddresses[i].mSelector)
507 case kAudioHardwarePropertyDefaultOutputDevice:
508 currentDefaultOutputDevice = CCoreAudioHardware::GetDefaultOutputDevice();
509 // This listener is called on every change of the hardware
510 // device. So check if the default device has really changed.
511 if (oldDefaultDevice != currentDefaultOutputDevice)
513 deviceChanged = true;
514 oldDefaultDevice = currentDefaultOutputDevice;
518 deviceChanged = true;
527 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - reenumerating");
528 CAEFactory::DeviceChange();
529 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - done");
534 void RegisterDeviceChangedCB(bool bRegister, void *ref)
536 OSStatus ret = noErr;
537 AudioObjectPropertyAddress inAdr =
539 kAudioHardwarePropertyDevices,
540 kAudioObjectPropertyScopeGlobal,
541 kAudioObjectPropertyElementMaster
546 ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
547 inAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
548 ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
552 ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
553 inAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
554 ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
558 CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");
562 ////////////////////////////////////////////////////////////////////////////////////////////
563 CAESinkDARWINOSX::CAESinkDARWINOSX()
564 : m_latentFrames(0), m_outputBitstream(false), m_outputBuffer(NULL), m_planar(false), m_planarBuffer(NULL), m_buffer(NULL)
566 // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
567 // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
568 // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
569 // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
570 CFRunLoopRef theRunLoop = NULL;
571 AudioObjectPropertyAddress theAddress = {
572 kAudioHardwarePropertyRunLoop,
573 kAudioObjectPropertyScopeGlobal,
574 kAudioObjectPropertyElementMaster
576 OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject,
577 &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
578 if (theError != noErr)
580 CLog::Log(LOGERROR, "CCoreAudioAE::constructor: kAudioHardwarePropertyRunLoop error.");
582 RegisterDeviceChangedCB(true, this);
587 CAESinkDARWINOSX::~CAESinkDARWINOSX()
589 RegisterDeviceChangedCB(false, this);
592 float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format)
595 if (format.m_dataFormat == AE_FMT_AC3 ||
596 format.m_dataFormat == AE_FMT_DTS)
598 if (desc.mFormatID == kAudioFormat60958AC3 ||
599 desc.mFormatID == 'IAC3' ||
600 desc.mFormatID == kAudioFormatAC3)
602 if (desc.mSampleRate == format.m_sampleRate &&
603 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
604 desc.mChannelsPerFrame == format.m_channelLayout.Count())
611 if (format.m_dataFormat == AE_FMT_AC3 ||
612 format.m_dataFormat == AE_FMT_DTS ||
613 format.m_dataFormat == AE_FMT_EAC3)
614 { // we should be able to bistreaming in PCM if the samplerate, bitdepth and channels match
615 if (desc.mSampleRate == format.m_sampleRate &&
616 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
617 desc.mChannelsPerFrame == format.m_channelLayout.Count() &&
618 desc.mFormatID == kAudioFormatLinearPCM)
624 { // non-passthrough, whatever works is fine
625 if (desc.mFormatID == kAudioFormatLinearPCM)
627 if (desc.mSampleRate == format.m_sampleRate)
629 else if (desc.mSampleRate > format.m_sampleRate)
631 if (desc.mChannelsPerFrame == format.m_channelLayout.Count())
633 else if (desc.mChannelsPerFrame > format.m_channelLayout.Count())
635 if (format.m_dataFormat == AE_FMT_FLOAT)
636 { // for float, prefer the highest bitdepth we have
637 if (desc.mBitsPerChannel >= 16)
638 score += (desc.mBitsPerChannel / 8);
642 if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
644 else if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
652 bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
654 AudioDeviceID deviceID = 0;
655 CADeviceList devices = GetDevices();
656 if (StringUtils::EqualsNoCase(device, "default"))
658 CCoreAudioHardware::GetOutputDeviceName(device);
659 deviceID = CCoreAudioHardware::GetDefaultOutputDevice();
660 CLog::Log(LOGNOTICE, "%s: Opening default device %s", __PRETTY_FUNCTION__, device.c_str());
664 for (size_t i = 0; i < devices.size(); i++)
666 if (device.find(devices[i].second.m_deviceName) != std::string::npos)
668 deviceID = devices[i].first;
676 CLog::Log(LOGERROR, "%s: Unable to find device %s", __FUNCTION__, device.c_str());
680 m_device.Open(deviceID);
682 // Fetch a list of the streams defined by the output device
683 AudioStreamIdList streams;
684 m_device.GetStreams(&streams);
686 CLog::Log(LOGDEBUG, "%s: Finding stream for format %s", __FUNCTION__, CAEUtil::DataFormatToStr(format.m_dataFormat));
688 bool passthrough = false;
689 UInt32 outputIndex = 0;
690 float outputScore = 0;
691 AudioStreamBasicDescription outputFormat = {0};
692 AudioStreamID outputStream = 0;
694 /* The theory is to score based on
695 1. Matching passthrough characteristics (i.e. passthrough flag)
696 2. Matching sample rate.
697 3. Matching bits per channel (or higher).
698 4. Matching number of channels (or higher).
701 for (AudioStreamIdList::const_iterator i = streams.begin(); i != streams.end(); ++i)
703 // Probe physical formats
704 StreamFormatList formats;
705 CCoreAudioStream::GetAvailablePhysicalFormats(*i, &formats);
706 for (StreamFormatList::const_iterator j = formats.begin(); j != formats.end(); ++j)
708 AudioStreamBasicDescription desc = j->mFormat;
710 // for devices with kAudioStreamAnyRate
711 // assume that the user uses a fixed config
712 // and knows what he is doing - so we use
713 // the requested samplerate here
714 if (desc.mSampleRate == kAudioStreamAnyRate)
715 desc.mSampleRate = format.m_sampleRate;
717 float score = ScoreStream(desc, format);
719 std::string formatString;
720 CLog::Log(LOGDEBUG, "%s: Physical Format: %s rated %f", __FUNCTION__, StreamDescriptionToString(desc, formatString), score);
722 if (score > outputScore)
724 passthrough = score > 1000;
735 if (streams.size() > 1 && outputFormat.mChannelsPerFrame == 1)
737 CLog::Log(LOGDEBUG, "%s Found planar audio with %u channels?", __FUNCTION__, streams.size());
738 outputFormat.mChannelsPerFrame = std::min((size_t)format.m_channelLayout.Count(), streams.size());
742 if (!outputFormat.mFormatID)
744 CLog::Log(LOGERROR, "%s, Unable to find suitable stream", __FUNCTION__);
748 /* Update our AE format */
749 format.m_sampleRate = outputFormat.mSampleRate;
751 m_outputBitstream = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM;
753 std::string formatString;
754 CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, (unsigned int)outputIndex, (unsigned int)outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : "");
756 SetHogMode(passthrough);
758 // Configure the output stream object
759 m_outputStream.Open(outputStream);
761 AudioStreamBasicDescription virtualFormat, previousPhysicalFormat;
762 m_outputStream.GetVirtualFormat(&virtualFormat);
763 m_outputStream.GetPhysicalFormat(&previousPhysicalFormat);
764 CLog::Log(LOGDEBUG, "%s: Previous Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
765 CLog::Log(LOGDEBUG, "%s: Previous Physical Format: %s", __FUNCTION__, StreamDescriptionToString(previousPhysicalFormat, formatString));
767 m_outputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
768 m_outputStream.GetVirtualFormat(&virtualFormat);
769 CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
770 CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString));
772 // update the channel map based on the new stream format
773 GetAEChannelMap(m_device, format.m_channelLayout, outputFormat.mChannelsPerFrame);
776 m_latentFrames = m_device.GetNumLatencyFrames();
777 m_latentFrames += m_outputStream.GetNumLatencyFrames();
779 /* TODO: Should we use the virtual format to determine our data format? */
780 format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
781 format.m_frames = m_device.GetBufferSize();
782 format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
784 if (m_outputBitstream)
786 m_outputBuffer = new int16_t[format.m_frameSamples];
787 /* TODO: Do we need this? */
788 m_device.SetNominalSampleRate(format.m_sampleRate);
792 m_planarBuffer = new float[format.m_frameSamples];
794 unsigned int num_buffers = 4;
795 m_buffer = new AERingBuffer(num_buffers * format.m_frames * format.m_frameSize);
796 CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (format.m_sampleRate * format.m_frameSize));
800 format.m_dataFormat = AE_FMT_S16NE;
802 format.m_dataFormat = AE_FMT_FLOAT;
804 // Register for data request callbacks from the driver and start
805 m_device.AddIOProc(renderCallback, this);
810 void CAESinkDARWINOSX::SetHogMode(bool on)
812 // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
813 // It appears that leaving this set will aslo restore the previous stream format when the
814 // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
815 // From the SDK docs: "If the AudioDevice is in a non-mixable mode, the HAL will automatically take hog mode on behalf of the first process to start an IOProc."
817 // Lock down the device. This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
818 // If it is attempted after the format change, there is a high likelihood of a deadlock
819 // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
822 // Auto-Hog does not always un-hog the device when changing back to a mixable mode.
823 // Handle this on our own until it is fixed.
824 CCoreAudioHardware::SetAutoHogMode(false);
825 bool autoHog = CCoreAudioHardware::GetAutoHogMode();
826 CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: "
827 "Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
831 m_device.SetHogStatus(on);
832 m_device.SetMixingSupport(!on);
835 void CAESinkDARWINOSX::Deinitialize()
838 m_device.RemoveIOProc();
840 m_outputStream.Close();
847 m_outputBitstream = false;
849 delete[] m_outputBuffer;
850 m_outputBuffer = NULL;
853 delete[] m_planarBuffer;
854 m_planarBuffer = NULL;
859 bool CAESinkDARWINOSX::IsCompatible(const AEAudioFormat &format, const std::string &device)
861 return ((m_format.m_sampleRate == format.m_sampleRate) &&
862 (m_format.m_dataFormat == format.m_dataFormat) &&
863 (m_format.m_channelLayout == format.m_channelLayout));
866 double CAESinkDARWINOSX::GetDelay()
870 // Calculate the duration of the data in the cache
871 double delay = (double)m_buffer->GetReadSize() / (double)m_format.m_frameSize;
872 delay += (double)m_latentFrames;
873 delay /= (double)m_format.m_sampleRate;
879 double CAESinkDARWINOSX::GetCacheTotal()
881 return (double)m_buffer->GetMaxSize() / (double)(m_format.m_frameSize * m_format.m_sampleRate);
884 CCriticalSection mutex;
885 XbmcThreads::ConditionVariable condVar;
887 unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
889 if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
890 { // no space to write - wait for a bit
891 CSingleLock lock(mutex);
892 unsigned int timeout = 900 * frames / m_format.m_sampleRate;
896 // we are using a timer here for beeing sure for timeouts
897 // condvar can be woken spuriously as signaled
898 XbmcThreads::EndTime timer(timeout);
899 condVar.wait(mutex, timeout);
900 if (!m_started && timer.IsTimePast())
902 CLog::Log(LOGERROR, "%s engine didn't start in %d ms!", __FUNCTION__, timeout);
907 unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
909 m_buffer->Write(data, write_frames * m_format.m_frameSize);
914 void CAESinkDARWINOSX::Drain()
916 int bytes = m_buffer->GetReadSize();
917 int totalBytes = bytes;
918 int maxNumTimeouts = 3;
919 unsigned int timeout = 900 * bytes / (m_format.m_sampleRate * m_format.m_frameSize);
920 while (bytes && maxNumTimeouts > 0)
922 CSingleLock lock(mutex);
923 XbmcThreads::EndTime timer(timeout);
924 condVar.wait(mutex, timeout);
926 bytes = m_buffer->GetReadSize();
927 // if we timeout and don't
928 // consum bytes - decrease maxNumTimeouts
929 if (timer.IsTimePast() && bytes == totalBytes)
935 void CAESinkDARWINOSX::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
939 for (CADeviceList::const_iterator i = s_devices.begin(); i != s_devices.end(); ++i)
940 list.push_back(i->second);
943 inline void LogLevel(unsigned int got, unsigned int wanted)
945 static unsigned int lastReported = INT_MAX;
948 if (got != lastReported)
950 CLog::Log(LOGWARNING, "DARWINOSX: %sflow (%u vs %u bytes)", got > wanted ? "over" : "under", got, wanted);
955 lastReported = INT_MAX; // indicate we were good at least once
958 OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
960 CAESinkDARWINOSX *sink = (CAESinkDARWINOSX*)inClientData;
962 sink->m_started = true;
965 unsigned int channels = std::min((unsigned int)outOutputData->mNumberBuffers, sink->m_format.m_channelLayout.Count());
966 unsigned int wanted = outOutputData->mBuffers[0].mDataByteSize;
967 unsigned int bytes = std::min(sink->m_buffer->GetReadSize() / channels, wanted);
968 sink->m_buffer->Read((unsigned char *)sink->m_planarBuffer, bytes * channels);
969 // transform from interleaved to planar
970 const float *src = sink->m_planarBuffer;
971 for (unsigned int i = 0; i < bytes / sizeof(float); i++)
973 for (unsigned int j = 0; j < channels; j++)
975 float *dst = (float *)outOutputData->mBuffers[j].mData;
979 LogLevel(bytes, wanted);
980 // tell the sink we're good for more data
985 for (unsigned int i = 0; i < outOutputData->mNumberBuffers; i++)
987 // NULL indicates a disabled stream
989 if (outOutputData->mBuffers[i].mData == NULL)
992 if (sink->m_outputBitstream)
994 /* HACK for bitstreaming AC3/DTS via PCM.
995 We reverse the float->S16LE conversion done in the stream or device */
996 static const float mul = 1.0f / (INT16_MAX + 1);
998 unsigned int wanted = std::min(outOutputData->mBuffers[i].mDataByteSize / sizeof(float), (size_t)sink->m_format.m_frameSamples) * sizeof(int16_t);
999 if (wanted <= sink->m_buffer->GetReadSize())
1001 sink->m_buffer->Read((unsigned char *)sink->m_outputBuffer, wanted);
1002 int16_t *src = sink->m_outputBuffer;
1003 float *dest = (float*)outOutputData->mBuffers[i].mData;
1004 for (unsigned int i = 0; i < wanted / 2; i++)
1005 *dest++ = *src++ * mul;
1010 /* buffers appear to come from CA already zero'd, so just copy what is wanted */
1011 unsigned int wanted = outOutputData->mBuffers[i].mDataByteSize;
1012 unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
1013 sink->m_buffer->Read((unsigned char*)outOutputData->mBuffers[i].mData, bytes);
1014 LogLevel(bytes, wanted);
1017 // tell the sink we're good for more data
1018 condVar.notifyAll();