[ios/osx] adds Darwin sinks for ActiveAE and drop the CoreAudio engine.
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / AESinkDARWINOSX.cpp
1 /*
2  *      Copyright (C) 2005-2014 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
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 "osx/DarwinUtils.h"
28 #include "utils/log.h"
29 #include "utils/StringUtils.h"
30 #include "threads/Condition.h"
31 #include "threads/CriticalSection.h"
32
33 #include <sstream>
34
35 #define CA_MAX_CHANNELS 8
36 static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = {
37   AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
38   AE_CH_NULL
39 };
40
41 static bool HasSampleRate(const AESampleRateList &list, const unsigned int samplerate)
42 {
43   for (size_t i = 0; i < list.size(); ++i)
44   {
45     if (list[i] == samplerate)
46       return true;
47   }
48   return false;
49 }
50
51 static bool HasDataFormat(const AEDataFormatList &list, const enum AEDataFormat format)
52 {
53   for (size_t i = 0; i < list.size(); ++i)
54   {
55     if (list[i] == format)
56       return true;
57   }
58   return false;
59 }
60
61 typedef std::vector< std::pair<AudioDeviceID, CAEDeviceInfo> > CADeviceList;
62
63 static void EnumerateDevices(CADeviceList &list)
64 {
65   CAEDeviceInfo device;
66
67   std::string defaultDeviceName;
68   CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
69
70   CoreAudioDeviceList deviceIDList;
71   CCoreAudioHardware::GetOutputDevices(&deviceIDList);
72   while (!deviceIDList.empty())
73   {
74     AudioDeviceID deviceID = deviceIDList.front();
75     CCoreAudioDevice caDevice(deviceID);
76
77     device.m_channels.Reset();
78     device.m_dataFormats.clear();
79     device.m_sampleRates.clear();
80
81     device.m_deviceType = AE_DEVTYPE_PCM;
82     device.m_deviceName = caDevice.GetName();
83     device.m_displayName = device.m_deviceName;
84     device.m_displayNameExtra = "";
85
86     if (device.m_deviceName.find("HDMI") != std::string::npos)
87       device.m_deviceType = AE_DEVTYPE_HDMI;
88
89     CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
90     AudioStreamIdList streams;
91     if (caDevice.GetStreams(&streams))
92     {
93       for (AudioStreamIdList::iterator j = streams.begin(); j != streams.end(); ++j)
94       {
95         StreamFormatList streams;
96         if (CCoreAudioStream::GetAvailablePhysicalFormats(*j, &streams))
97         {
98           for (StreamFormatList::iterator i = streams.begin(); i != streams.end(); ++i)
99           {
100             AudioStreamBasicDescription desc = i->mFormat;
101             std::string formatString;
102             CLog::Log(LOGDEBUG, "EnumerateDevices:Format(%s)" ,
103                                 StreamDescriptionToString(desc, formatString));
104             // add stream format info
105             switch (desc.mFormatID)
106             {
107               case kAudioFormatAC3:
108               case kAudioFormat60958AC3:
109                 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
110                   device.m_dataFormats.push_back(AE_FMT_AC3);
111                 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
112                   device.m_dataFormats.push_back(AE_FMT_DTS);
113                 // if we are not hdmi, this is an S/PDIF device
114                 if (device.m_deviceType != AE_DEVTYPE_HDMI)
115                   device.m_deviceType = AE_DEVTYPE_IEC958;
116                 break;
117               default:
118                 AEDataFormat format = AE_FMT_INVALID;
119                 switch(desc.mBitsPerChannel)
120                 {
121                   case 16:
122                     if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
123                       format = AE_FMT_S16BE;
124                     else
125                     {
126                       /* Passthrough is possible with a 2ch digital output */
127                       if (desc.mChannelsPerFrame == 2 && CCoreAudioStream::IsDigitalOuptut(*j))
128                       {
129                         if (desc.mSampleRate == 48000)
130                         {
131                           if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
132                             device.m_dataFormats.push_back(AE_FMT_AC3);
133                           if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
134                             device.m_dataFormats.push_back(AE_FMT_DTS);
135                         }
136                         else if (desc.mSampleRate == 192000)
137                         {
138                           if (!HasDataFormat(device.m_dataFormats, AE_FMT_EAC3))
139                             device.m_dataFormats.push_back(AE_FMT_EAC3);
140                         }
141                       }
142                       format = AE_FMT_S16LE;
143                     }
144                     break;
145                   case 24:
146                     if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
147                       format = AE_FMT_S24BE3;
148                     else
149                       format = AE_FMT_S24LE3;
150                     break;
151                   case 32:
152                     if (desc.mFormatFlags & kAudioFormatFlagIsFloat)
153                       format = AE_FMT_FLOAT;
154                     else
155                     {
156                       if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
157                         format = AE_FMT_S32BE;
158                       else
159                         format = AE_FMT_S32LE;
160                     }
161                     break;
162                 }
163                 if (format != AE_FMT_INVALID && !HasDataFormat(device.m_dataFormats, format))
164                   device.m_dataFormats.push_back(format);
165                 break;
166             }
167
168             // add channel info
169             CAEChannelInfo channel_info;
170             for (UInt32 chan = 0; chan < CA_MAX_CHANNELS && chan < desc.mChannelsPerFrame; ++chan)
171             {
172               if (!device.m_channels.HasChannel(CAChannelMap[chan]))
173                 device.m_channels += CAChannelMap[chan];
174               channel_info += CAChannelMap[chan];
175             }
176
177             // add sample rate info
178             if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
179               device.m_sampleRates.push_back(desc.mSampleRate);
180           }
181         }
182       }
183     }
184
185     list.push_back(std::make_pair(deviceID, device));
186     //in the first place of the list add the default device
187     //with name "default" - if this is selected
188     //we will output to whatever osx claims to be default
189     //(allows transition from headphones to speaker and stuff
190     //like that
191     if(defaultDeviceName == device.m_deviceName)
192     {
193       device.m_deviceName = "default";
194       device.m_displayName = "Default";
195       list.insert(list.begin(), std::make_pair(deviceID, device));
196     }
197
198     deviceIDList.pop_front();
199   }
200 }
201
202 /* static, threadsafe access to the device list */
203 static CADeviceList     s_devices;
204 static CCriticalSection s_devicesLock;
205
206 static void EnumerateDevices()
207 {
208   CADeviceList devices;
209   EnumerateDevices(devices);
210   {
211     CSingleLock lock(s_devicesLock);
212     s_devices = devices;
213   }
214 }
215
216 static CADeviceList GetDevices()
217 {
218   CADeviceList list;
219   {
220     CSingleLock lock(s_devicesLock);
221     list = s_devices;
222   }
223   return list;
224 }
225
226 OSStatus deviceChangedCB(AudioObjectID                       inObjectID,
227                          UInt32                              inNumberAddresses,
228                          const AudioObjectPropertyAddress    inAddresses[],
229                          void*                               inClientData)
230 {
231   CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - reenumerating");
232   CAEFactory::DeviceChange();
233   CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - done");
234   return noErr;
235 }
236
237 void RegisterDeviceChangedCB(bool bRegister, void *ref)
238 {
239   OSStatus ret = noErr;
240   const AudioObjectPropertyAddress inAdr =
241   {
242     kAudioHardwarePropertyDevices,
243     kAudioObjectPropertyScopeGlobal,
244     kAudioObjectPropertyElementMaster
245   };
246
247   if (bRegister)
248     ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
249   else
250     ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
251
252   if (ret != noErr)
253     CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");
254 }
255
256
257 ////////////////////////////////////////////////////////////////////////////////////////////
258 CAESinkDARWINOSX::CAESinkDARWINOSX()
259 : m_latentFrames(0), m_outputBufferIndex(0), m_outputBitstream(false), m_outputBuffer(NULL), m_buffer(NULL)
260 {
261   // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
262   // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
263   // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
264   // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
265   CFRunLoopRef theRunLoop = NULL;
266   AudioObjectPropertyAddress theAddress = {
267     kAudioHardwarePropertyRunLoop,
268     kAudioObjectPropertyScopeGlobal,
269     kAudioObjectPropertyElementMaster
270   };
271   OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject,
272                                                  &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
273   if (theError != noErr)
274   {
275     CLog::Log(LOGERROR, "CCoreAudioAE::constructor: kAudioHardwarePropertyRunLoop error.");
276   }
277   RegisterDeviceChangedCB(true, this);
278   m_started = false;
279 }
280
281 CAESinkDARWINOSX::~CAESinkDARWINOSX()
282 {
283   RegisterDeviceChangedCB(false, this);
284 }
285
286 float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format)
287 {
288   float score = 0;
289   if (format.m_dataFormat == AE_FMT_AC3 ||
290       format.m_dataFormat == AE_FMT_DTS)
291   {
292     if (desc.mFormatID == kAudioFormat60958AC3 ||
293         desc.mFormatID == 'IAC3' ||
294         desc.mFormatID == kAudioFormatAC3)
295     {
296       if (desc.mSampleRate == format.m_sampleRate &&
297           desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
298           desc.mChannelsPerFrame == format.m_channelLayout.Count())
299       {
300         // perfect match
301         score = FLT_MAX;
302       }
303     }
304   }
305   if (format.m_dataFormat == AE_FMT_AC3 ||
306       format.m_dataFormat == AE_FMT_DTS ||
307       format.m_dataFormat == AE_FMT_EAC3)
308   { // we should be able to bistreaming in PCM if the samplerate, bitdepth and channels match
309     if (desc.mSampleRate       == format.m_sampleRate                            &&
310         desc.mBitsPerChannel   == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
311         desc.mChannelsPerFrame == format.m_channelLayout.Count()                 &&
312         desc.mFormatID         == kAudioFormatLinearPCM)
313     {
314       score = FLT_MAX / 2;
315     }
316   }
317   else
318   { // non-passthrough, whatever works is fine
319     if (desc.mFormatID == kAudioFormatLinearPCM)
320     {
321       if (desc.mSampleRate == format.m_sampleRate)
322         score += 10;
323       else if (desc.mSampleRate > format.m_sampleRate)
324         score += 1;
325       if (desc.mChannelsPerFrame == format.m_channelLayout.Count())
326         score += 5;
327       else if (desc.mChannelsPerFrame > format.m_channelLayout.Count())
328         score += 1;
329       if (format.m_dataFormat == AE_FMT_FLOAT)
330       { // for float, prefer the highest bitdepth we have
331         if (desc.mBitsPerChannel >= 16)
332           score += (desc.mBitsPerChannel / 8);
333       }
334       else
335       {
336         if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
337           score += 5;
338         else if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
339           score += 1;
340       }
341     }
342   }
343   return score;
344 }
345
346 bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
347 {
348   AudioDeviceID deviceID = 0;
349   CADeviceList devices = GetDevices();
350   if (StringUtils::EqualsNoCase(device, "default"))
351   {
352     CCoreAudioHardware::GetOutputDeviceName(device);
353     CLog::Log(LOGNOTICE, "%s: Opening default device %s", __PRETTY_FUNCTION__, device.c_str());
354   }
355       
356   for (size_t i = 0; i < devices.size(); i++)
357   {
358     if (device.find(devices[i].second.m_deviceName) != std::string::npos)
359     {
360       m_info = devices[i].second;
361       deviceID = devices[i].first;
362       break;
363     }
364   }
365   if (!deviceID)
366   {
367     CLog::Log(LOGERROR, "%s: Unable to find device %s", __FUNCTION__, device.c_str());
368     return false;
369   }
370
371   m_device.Open(deviceID);
372
373   // Fetch a list of the streams defined by the output device
374   AudioStreamIdList streams;
375   m_device.GetStreams(&streams);
376
377   CLog::Log(LOGDEBUG, "%s: Finding stream for format %s", __FUNCTION__, CAEUtil::DataFormatToStr(format.m_dataFormat));
378
379   bool                        passthrough  = false;
380   UInt32                      outputIndex  = 0;
381   float                       outputScore  = 0;
382   AudioStreamBasicDescription outputFormat = {0};
383   AudioStreamID               outputStream = 0;
384
385   /* The theory is to score based on
386    1. Matching passthrough characteristics (i.e. passthrough flag)
387    2. Matching sample rate.
388    3. Matching bits per channel (or higher).
389    4. Matching number of channels (or higher).
390    */
391   UInt32 index = 0;
392   for (AudioStreamIdList::const_iterator i = streams.begin(); i != streams.end(); ++i)
393   {
394     // Probe physical formats
395     StreamFormatList formats;
396     CCoreAudioStream::GetAvailablePhysicalFormats(*i, &formats);
397     for (StreamFormatList::const_iterator j = formats.begin(); j != formats.end(); ++j)
398     {
399       const AudioStreamBasicDescription &desc = j->mFormat;
400
401       float score = ScoreStream(desc, format);
402
403       std::string formatString;
404       CLog::Log(LOGDEBUG, "%s: Physical Format: %s rated %f", __FUNCTION__, StreamDescriptionToString(desc, formatString), score);
405
406       if (score > outputScore)
407       {
408         passthrough  = score > 1000;
409         outputScore  = score;
410         outputFormat = desc;
411         outputStream = *i;
412         outputIndex  = index;
413       }
414     }
415     index++;
416   }
417
418   if (!outputFormat.mFormatID)
419   {
420     CLog::Log(LOGERROR, "%s, Unable to find suitable stream", __FUNCTION__);
421     return false;
422   }
423
424   /* Update our AE format */
425   format.m_sampleRate    = outputFormat.mSampleRate;
426   if (outputFormat.mChannelsPerFrame != format.m_channelLayout.Count())
427   { /* update the channel count.  We assume that they're layed out as given in CAChannelMap.
428        if they're not, this is plain wrong */
429     format.m_channelLayout.Reset();
430     for (unsigned int i = 0; i < outputFormat.mChannelsPerFrame; i++)
431       format.m_channelLayout += CAChannelMap[i];
432   }
433
434   m_outputBufferIndex = outputIndex;
435   m_outputBitstream   = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM;
436
437   std::string formatString;
438   CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, m_outputBufferIndex, outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : "");
439
440   SetHogMode(passthrough);
441
442   // Configure the output stream object
443   m_outputStream.Open(outputStream);
444
445   AudioStreamBasicDescription virtualFormat, previousPhysicalFormat;
446   m_outputStream.GetVirtualFormat(&virtualFormat);
447   m_outputStream.GetPhysicalFormat(&previousPhysicalFormat);
448   CLog::Log(LOGDEBUG, "%s: Previous Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
449   CLog::Log(LOGDEBUG, "%s: Previous Physical Format: %s", __FUNCTION__, StreamDescriptionToString(previousPhysicalFormat, formatString));
450
451   m_outputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
452   m_outputStream.GetVirtualFormat(&virtualFormat);
453   CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
454   CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString));
455
456   m_latentFrames = m_device.GetNumLatencyFrames();
457   m_latentFrames += m_outputStream.GetNumLatencyFrames();
458
459   /* TODO: Should we use the virtual format to determine our data format? */
460   format.m_frameSize     = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
461   format.m_frames        = m_device.GetBufferSize();
462   format.m_frameSamples  = format.m_frames * format.m_channelLayout.Count();
463
464   if (m_outputBitstream)
465   {
466     m_outputBuffer = new int16_t[format.m_frameSamples];
467     /* TODO: Do we need this? */
468     m_device.SetNominalSampleRate(format.m_sampleRate);
469   }
470
471   unsigned int num_buffers = 4;
472   m_buffer = new AERingBuffer(num_buffers * format.m_frames * format.m_frameSize);
473   CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (format.m_sampleRate * format.m_frameSize));
474
475   m_format = format;
476   if (passthrough)
477     format.m_dataFormat = AE_FMT_S16NE;
478   else
479     format.m_dataFormat = AE_FMT_FLOAT;
480
481   // Register for data request callbacks from the driver and start
482   m_device.AddIOProc(renderCallback, this);
483   m_device.Start();
484   return true;
485 }
486
487 void CAESinkDARWINOSX::SetHogMode(bool on)
488 {
489   // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
490   // It appears that leaving this set will aslo restore the previous stream format when the
491   // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
492   // 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."
493
494   // Lock down the device.  This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
495   // If it is attempted after the format change, there is a high likelihood of a deadlock
496   // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
497   if (on)
498   {
499     // Auto-Hog does not always un-hog the device when changing back to a mixable mode.
500     // Handle this on our own until it is fixed.
501     CCoreAudioHardware::SetAutoHogMode(false);
502     bool autoHog = CCoreAudioHardware::GetAutoHogMode();
503     CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: "
504               "Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
505     if (autoHog)
506       return;
507   }
508   m_device.SetHogStatus(on);
509   m_device.SetMixingSupport(!on);
510 }
511
512 void CAESinkDARWINOSX::Deinitialize()
513 {
514   m_device.Stop();
515   m_device.RemoveIOProc();
516
517   m_outputStream.Close();
518   m_device.Close();
519   if (m_buffer)
520   {
521     delete m_buffer;
522     m_buffer = NULL;
523   }
524   m_outputBufferIndex = 0;
525   m_outputBitstream = false;
526
527   delete[] m_outputBuffer;
528   m_outputBuffer = NULL;
529
530   m_started = false;
531 }
532
533 bool CAESinkDARWINOSX::IsCompatible(const AEAudioFormat &format, const std::string &device)
534 {
535   return ((m_format.m_sampleRate    == format.m_sampleRate) &&
536           (m_format.m_dataFormat    == format.m_dataFormat) &&
537           (m_format.m_channelLayout == format.m_channelLayout));
538 }
539
540 double CAESinkDARWINOSX::GetDelay()
541 {
542   if (m_buffer)
543   {
544     // Calculate the duration of the data in the cache
545     double delay = (double)m_buffer->GetReadSize() / (double)m_format.m_frameSize;
546     delay += (double)m_latentFrames;
547     delay /= (double)m_format.m_sampleRate;
548     return delay;
549   }
550   return 0.0;
551 }
552
553 double CAESinkDARWINOSX::GetCacheTotal()
554 {
555   return (double)m_buffer->GetMaxSize() / (double)(m_format.m_frameSize * m_format.m_sampleRate);
556 }
557
558 CCriticalSection mutex;
559 XbmcThreads::ConditionVariable condVar;
560
561 unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
562 {
563   if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
564   { // no space to write - wait for a bit
565     CSingleLock lock(mutex);
566     if (!m_started)
567       condVar.wait(lock);
568     else
569       condVar.wait(lock, 900 * frames / m_format.m_sampleRate);
570   }
571
572   unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
573   if (write_frames)
574     m_buffer->Write(data, write_frames * m_format.m_frameSize);
575
576   return write_frames;
577 }
578
579 void CAESinkDARWINOSX::Drain()
580 {
581   CCriticalSection mutex;
582   int bytes = m_buffer->GetReadSize();
583   while (bytes)
584   {
585     CSingleLock lock(mutex);
586     condVar.wait(mutex, 900 * bytes / (m_format.m_sampleRate * m_format.m_frameSize));
587     bytes = m_buffer->GetReadSize();
588   }
589 }
590
591 void CAESinkDARWINOSX::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
592 {
593   EnumerateDevices();
594   list.clear();
595   for (CADeviceList::const_iterator i = s_devices.begin(); i != s_devices.end(); ++i)
596     list.push_back(i->second);
597 }
598
599 OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
600 {
601   CAESinkDARWINOSX *sink = (CAESinkDARWINOSX*)inClientData;
602
603   sink->m_started = true;
604   if (sink->m_outputBufferIndex < outOutputData->mNumberBuffers)
605   {
606     if (sink->m_outputBitstream)
607     {
608       /* HACK for bitstreaming AC3/DTS via PCM.
609        We reverse the float->S16LE conversion done in the stream or device */
610       static const float mul = 1.0f / (INT16_MAX + 1);
611
612       unsigned int wanted = std::min(outOutputData->mBuffers[sink->m_outputBufferIndex].mDataByteSize / sizeof(float), (size_t)sink->m_format.m_frameSamples)  * sizeof(int16_t);
613       if (wanted <= sink->m_buffer->GetReadSize())
614       {
615         sink->m_buffer->Read((unsigned char *)sink->m_outputBuffer, wanted);
616         int16_t *src = sink->m_outputBuffer;
617         float  *dest = (float*)outOutputData->mBuffers[sink->m_outputBufferIndex].mData;
618         for (unsigned int i = 0; i < wanted / 2; i++)
619           *dest++ = *src++ * mul;
620       }
621     }
622     else
623     {
624       /* buffers appear to come from CA already zero'd, so just copy what is wanted */
625       unsigned int wanted = outOutputData->mBuffers[sink->m_outputBufferIndex].mDataByteSize;
626       unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
627       sink->m_buffer->Read((unsigned char*)outOutputData->mBuffers[sink->m_outputBufferIndex].mData, bytes);
628       if (bytes != wanted)
629         CLog::Log(LOGERROR, "%s: %sFLOW (%i vs %i) bytes", __FUNCTION__, bytes > wanted ? "OVER" : "UNDER", bytes, wanted);
630     }
631
632     // tell the sink we're good for more data
633     condVar.notifyAll();
634   }
635   return noErr;
636 }