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;
442 //treat all other digital devices as HDMI to let options open to the user
443 if (device.m_deviceType == AE_DEVTYPE_PCM)
444 device.m_deviceType = AE_DEVTYPE_HDMI;
447 // devicename based overwrites from former code - maybe FIXME at some point when we
448 // are sure that the upper detection does its job in all[tm] use cases
450 device.m_deviceType = AE_DEVTYPE_HDMI;
451 if (hasDisplayPortName)
452 device.m_deviceType = AE_DEVTYPE_DP;
454 //get channel map to match the devices channel layout as set in audio-midi-setup
455 GetAEChannelMap(caDevice, device.m_channels, caDevice.GetTotalOutputChannels());
457 list.push_back(std::make_pair(deviceID, device));
458 //in the first place of the list add the default device
459 //with name "default" - if this is selected
460 //we will output to whatever osx claims to be default
461 //(allows transition from headphones to speaker and stuff
463 if(defaultDeviceName == device.m_deviceName)
465 device.m_deviceName = "default";
466 device.m_displayName = "Default";
467 list.insert(list.begin(), std::make_pair(deviceID, device));
470 deviceIDList.pop_front();
474 /* static, threadsafe access to the device list */
475 static CADeviceList s_devices;
476 static CCriticalSection s_devicesLock;
478 static void EnumerateDevices()
480 CADeviceList devices;
481 EnumerateDevices(devices);
483 CSingleLock lock(s_devicesLock);
488 static CADeviceList GetDevices()
492 CSingleLock lock(s_devicesLock);
498 OSStatus deviceChangedCB(AudioObjectID inObjectID,
499 UInt32 inNumberAddresses,
500 const AudioObjectPropertyAddress inAddresses[],
503 bool deviceChanged = false;
504 static AudioDeviceID oldDefaultDevice = 0;
505 AudioDeviceID currentDefaultOutputDevice = 0;
507 for (unsigned int i = 0; i < inNumberAddresses; i++)
509 switch (inAddresses[i].mSelector)
511 case kAudioHardwarePropertyDefaultOutputDevice:
512 currentDefaultOutputDevice = CCoreAudioHardware::GetDefaultOutputDevice();
513 // This listener is called on every change of the hardware
514 // device. So check if the default device has really changed.
515 if (oldDefaultDevice != currentDefaultOutputDevice)
517 deviceChanged = true;
518 oldDefaultDevice = currentDefaultOutputDevice;
522 deviceChanged = true;
531 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - reenumerating");
532 CAEFactory::DeviceChange();
533 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - done");
538 ////////////////////////////////////////////////////////////////////////////////////////////
539 CAESinkDARWINOSX::CAESinkDARWINOSX()
540 : m_latentFrames(0), m_outputBitstream(false), m_outputBuffer(NULL), m_planar(false), m_planarBuffer(NULL), m_buffer(NULL)
542 // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
543 // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
544 // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
545 // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
546 CFRunLoopRef theRunLoop = NULL;
547 AudioObjectPropertyAddress theAddress = {
548 kAudioHardwarePropertyRunLoop,
549 kAudioObjectPropertyScopeGlobal,
550 kAudioObjectPropertyElementMaster
552 OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject,
553 &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
554 if (theError != noErr)
556 CLog::Log(LOGERROR, "CCoreAudioAE::constructor: kAudioHardwarePropertyRunLoop error.");
558 CCoreAudioDevice::RegisterDeviceChangedCB(true, deviceChangedCB, this);
559 CCoreAudioDevice::RegisterDefaultOutputDeviceChangedCB(true, deviceChangedCB, this);
564 CAESinkDARWINOSX::~CAESinkDARWINOSX()
566 CCoreAudioDevice::RegisterDeviceChangedCB(false, deviceChangedCB, this);
567 CCoreAudioDevice::RegisterDefaultOutputDeviceChangedCB(false, deviceChangedCB, this);
570 float scoreSampleRate(Float64 destinationRate, unsigned int sourceRate)
574 double fracPortion = modf(destinationRate / sourceRate, &intPortion);
576 score += (1 - fracPortion) * 1000; // prefer sample rates that are multiples of the source sample rate
577 score += (intPortion == 1.0) ? 500 : 0; // prefer exact matches over other multiples
578 score += (intPortion > 1 && intPortion < 100) ? (100 - intPortion) / 100 * 100 : 0; // prefer smaller multiples otherwise
583 float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format)
586 if (format.m_dataFormat == AE_FMT_AC3 ||
587 format.m_dataFormat == AE_FMT_DTS)
589 if (desc.mFormatID == kAudioFormat60958AC3 ||
590 desc.mFormatID == 'IAC3' ||
591 desc.mFormatID == kAudioFormatAC3)
593 if (desc.mSampleRate == format.m_sampleRate &&
594 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
595 desc.mChannelsPerFrame == format.m_channelLayout.Count())
602 if (format.m_dataFormat == AE_FMT_AC3 ||
603 format.m_dataFormat == AE_FMT_DTS ||
604 format.m_dataFormat == AE_FMT_EAC3)
605 { // we should be able to bistreaming in PCM if the samplerate, bitdepth and channels match
606 if (desc.mSampleRate == format.m_sampleRate &&
607 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
608 desc.mChannelsPerFrame == format.m_channelLayout.Count() &&
609 desc.mFormatID == kAudioFormatLinearPCM)
615 { // non-passthrough, whatever works is fine
616 if (desc.mFormatID == kAudioFormatLinearPCM)
618 score += scoreSampleRate(desc.mSampleRate, format.m_sampleRate);
620 if (desc.mChannelsPerFrame == format.m_channelLayout.Count())
622 else if (desc.mChannelsPerFrame > format.m_channelLayout.Count())
624 if (format.m_dataFormat == AE_FMT_FLOAT)
625 { // for float, prefer the highest bitdepth we have
626 if (desc.mBitsPerChannel >= 16)
627 score += (desc.mBitsPerChannel / 8);
631 if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
633 else if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
641 bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
643 AudioDeviceID deviceID = 0;
644 CADeviceList devices = GetDevices();
645 if (StringUtils::EqualsNoCase(device, "default"))
647 CCoreAudioHardware::GetOutputDeviceName(device);
648 deviceID = CCoreAudioHardware::GetDefaultOutputDevice();
649 CLog::Log(LOGNOTICE, "%s: Opening default device %s", __PRETTY_FUNCTION__, device.c_str());
653 for (size_t i = 0; i < devices.size(); i++)
655 if (device.find(devices[i].second.m_deviceName) != std::string::npos)
657 deviceID = devices[i].first;
665 CLog::Log(LOGERROR, "%s: Unable to find device %s", __FUNCTION__, device.c_str());
669 m_device.Open(deviceID);
671 // Fetch a list of the streams defined by the output device
672 AudioStreamIdList streams;
673 m_device.GetStreams(&streams);
675 CLog::Log(LOGDEBUG, "%s: Finding stream for format %s", __FUNCTION__, CAEUtil::DataFormatToStr(format.m_dataFormat));
677 bool passthrough = false;
678 UInt32 outputIndex = 0;
679 UInt32 numOutputChannels = 0;
680 float outputScore = 0;
681 AudioStreamBasicDescription outputFormat = {0};
682 AudioStreamID outputStream = 0;
684 /* The theory is to score based on
685 1. Matching passthrough characteristics (i.e. passthrough flag)
686 2. Matching sample rate.
687 3. Matching bits per channel (or higher).
688 4. Matching number of channels (or higher).
691 for (AudioStreamIdList::const_iterator i = streams.begin(); i != streams.end(); ++i)
693 // Probe physical formats
694 StreamFormatList formats;
695 CCoreAudioStream::GetAvailablePhysicalFormats(*i, &formats);
696 for (StreamFormatList::const_iterator j = formats.begin(); j != formats.end(); ++j)
698 AudioStreamBasicDescription desc = j->mFormat;
700 // for devices with kAudioStreamAnyRate
701 // assume that the user uses a fixed config
702 // and knows what he is doing - so we use
703 // the requested samplerate here
704 if (desc.mSampleRate == kAudioStreamAnyRate)
705 desc.mSampleRate = format.m_sampleRate;
707 float score = ScoreStream(desc, format);
709 std::string formatString;
710 CLog::Log(LOGDEBUG, "%s: Physical Format: %s rated %f", __FUNCTION__, StreamDescriptionToString(desc, formatString), score);
712 if (score > outputScore)
714 passthrough = score > 10000;
725 numOutputChannels = outputFormat.mChannelsPerFrame;
726 if (streams.size() > 1 && outputFormat.mChannelsPerFrame == 1)
728 numOutputChannels = std::min((size_t)format.m_channelLayout.Count(), streams.size());
730 CLog::Log(LOGDEBUG, "%s Found planar audio with %u channels using %u of them.", __FUNCTION__, (unsigned int)streams.size(), (unsigned int)numOutputChannels);
733 if (!outputFormat.mFormatID)
735 CLog::Log(LOGERROR, "%s, Unable to find suitable stream", __FUNCTION__);
739 /* Update our AE format */
740 format.m_sampleRate = outputFormat.mSampleRate;
742 m_outputBitstream = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM;
744 std::string formatString;
745 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" : "");
747 SetHogMode(passthrough);
749 // Configure the output stream object
750 m_outputStream.Open(outputStream);
752 AudioStreamBasicDescription virtualFormat, previousPhysicalFormat;
753 m_outputStream.GetVirtualFormat(&virtualFormat);
754 m_outputStream.GetPhysicalFormat(&previousPhysicalFormat);
755 CLog::Log(LOGDEBUG, "%s: Previous Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
756 CLog::Log(LOGDEBUG, "%s: Previous Physical Format: %s", __FUNCTION__, StreamDescriptionToString(previousPhysicalFormat, formatString));
758 m_outputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
759 m_outputStream.GetVirtualFormat(&virtualFormat);
760 CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
761 CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString));
763 // update the channel map based on the new stream format
764 GetAEChannelMap(m_device, format.m_channelLayout, numOutputChannels);
767 m_latentFrames = m_device.GetNumLatencyFrames();
768 m_latentFrames += m_outputStream.GetNumLatencyFrames();
770 /* TODO: Should we use the virtual format to determine our data format? */
771 format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
772 format.m_frames = m_device.GetBufferSize();
773 format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
775 if (m_outputBitstream)
777 m_outputBuffer = new int16_t[format.m_frameSamples];
778 /* TODO: Do we need this? */
779 m_device.SetNominalSampleRate(format.m_sampleRate);
783 m_planarBuffer = new float[format.m_frameSamples];
785 unsigned int num_buffers = 4;
786 m_buffer = new AERingBuffer(num_buffers * format.m_frames * format.m_frameSize);
787 CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (format.m_sampleRate * format.m_frameSize));
791 format.m_dataFormat = AE_FMT_S16NE;
793 format.m_dataFormat = AE_FMT_FLOAT;
795 // Register for data request callbacks from the driver and start
796 m_device.AddIOProc(renderCallback, this);
801 void CAESinkDARWINOSX::SetHogMode(bool on)
803 // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
804 // It appears that leaving this set will aslo restore the previous stream format when the
805 // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
806 // 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."
808 // Lock down the device. This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
809 // If it is attempted after the format change, there is a high likelihood of a deadlock
810 // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
813 // Auto-Hog does not always un-hog the device when changing back to a mixable mode.
814 // Handle this on our own until it is fixed.
815 CCoreAudioHardware::SetAutoHogMode(false);
816 bool autoHog = CCoreAudioHardware::GetAutoHogMode();
817 CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: "
818 "Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
822 m_device.SetHogStatus(on);
823 m_device.SetMixingSupport(!on);
826 void CAESinkDARWINOSX::Deinitialize()
829 m_device.RemoveIOProc();
831 m_outputStream.Close();
838 m_outputBitstream = false;
840 delete[] m_outputBuffer;
841 m_outputBuffer = NULL;
844 delete[] m_planarBuffer;
845 m_planarBuffer = NULL;
850 bool CAESinkDARWINOSX::IsCompatible(const AEAudioFormat &format, const std::string &device)
852 return ((m_format.m_sampleRate == format.m_sampleRate) &&
853 (m_format.m_dataFormat == format.m_dataFormat) &&
854 (m_format.m_channelLayout == format.m_channelLayout));
857 double CAESinkDARWINOSX::GetDelay()
861 // Calculate the duration of the data in the cache
862 double delay = (double)m_buffer->GetReadSize() / (double)m_format.m_frameSize;
863 delay += (double)m_latentFrames;
864 delay /= (double)m_format.m_sampleRate;
870 double CAESinkDARWINOSX::GetCacheTotal()
872 return (double)m_buffer->GetMaxSize() / (double)(m_format.m_frameSize * m_format.m_sampleRate);
875 CCriticalSection mutex;
876 XbmcThreads::ConditionVariable condVar;
878 unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
880 if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
881 { // no space to write - wait for a bit
882 CSingleLock lock(mutex);
883 unsigned int timeout = 900 * frames / m_format.m_sampleRate;
887 // we are using a timer here for beeing sure for timeouts
888 // condvar can be woken spuriously as signaled
889 XbmcThreads::EndTime timer(timeout);
890 condVar.wait(mutex, timeout);
891 if (!m_started && timer.IsTimePast())
893 CLog::Log(LOGERROR, "%s engine didn't start in %d ms!", __FUNCTION__, timeout);
898 unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
900 m_buffer->Write(data, write_frames * m_format.m_frameSize);
905 void CAESinkDARWINOSX::Drain()
907 int bytes = m_buffer->GetReadSize();
908 int totalBytes = bytes;
909 int maxNumTimeouts = 3;
910 unsigned int timeout = 900 * bytes / (m_format.m_sampleRate * m_format.m_frameSize);
911 while (bytes && maxNumTimeouts > 0)
913 CSingleLock lock(mutex);
914 XbmcThreads::EndTime timer(timeout);
915 condVar.wait(mutex, timeout);
917 bytes = m_buffer->GetReadSize();
918 // if we timeout and don't
919 // consum bytes - decrease maxNumTimeouts
920 if (timer.IsTimePast() && bytes == totalBytes)
926 void CAESinkDARWINOSX::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
930 for (CADeviceList::const_iterator i = s_devices.begin(); i != s_devices.end(); ++i)
931 list.push_back(i->second);
934 inline void LogLevel(unsigned int got, unsigned int wanted)
936 static unsigned int lastReported = INT_MAX;
939 if (got != lastReported)
941 CLog::Log(LOGWARNING, "DARWINOSX: %sflow (%u vs %u bytes)", got > wanted ? "over" : "under", got, wanted);
946 lastReported = INT_MAX; // indicate we were good at least once
949 OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
951 CAESinkDARWINOSX *sink = (CAESinkDARWINOSX*)inClientData;
953 sink->m_started = true;
956 unsigned int channels = std::min((unsigned int)outOutputData->mNumberBuffers, sink->m_format.m_channelLayout.Count());
957 unsigned int wanted = outOutputData->mBuffers[0].mDataByteSize;
958 unsigned int bytes = std::min(sink->m_buffer->GetReadSize() / channels, wanted);
959 sink->m_buffer->Read((unsigned char *)sink->m_planarBuffer, bytes * channels);
960 // transform from interleaved to planar
961 const float *src = sink->m_planarBuffer;
962 for (unsigned int i = 0; i < bytes / sizeof(float); i++)
964 for (unsigned int j = 0; j < channels; j++)
966 float *dst = (float *)outOutputData->mBuffers[j].mData;
970 LogLevel(bytes, wanted);
971 // tell the sink we're good for more data
976 for (unsigned int i = 0; i < outOutputData->mNumberBuffers; i++)
978 // NULL indicates a disabled stream
980 if (outOutputData->mBuffers[i].mData == NULL)
983 if (sink->m_outputBitstream)
985 /* HACK for bitstreaming AC3/DTS via PCM.
986 We reverse the float->S16LE conversion done in the stream or device */
987 static const float mul = 1.0f / (INT16_MAX + 1);
989 unsigned int wanted = std::min(outOutputData->mBuffers[i].mDataByteSize / sizeof(float), (size_t)sink->m_format.m_frameSamples) * sizeof(int16_t);
990 if (wanted <= sink->m_buffer->GetReadSize())
992 sink->m_buffer->Read((unsigned char *)sink->m_outputBuffer, wanted);
993 int16_t *src = sink->m_outputBuffer;
994 float *dest = (float*)outOutputData->mBuffers[i].mData;
995 for (unsigned int i = 0; i < wanted / 2; i++)
996 *dest++ = *src++ * mul;
1001 /* buffers appear to come from CA already zero'd, so just copy what is wanted */
1002 unsigned int wanted = outOutputData->mBuffers[i].mDataByteSize;
1003 unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
1004 sink->m_buffer->Read((unsigned char*)outOutputData->mBuffers[i].mData, bytes);
1005 LogLevel(bytes, wanted);
1008 // tell the sink we're good for more data
1009 condVar.notifyAll();