[AESink/ios] - fix drain - we need to output silence when no samples are left - else...
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / AESinkDARWINIOS.cpp
1 /*
2  *      Copyright (C) 2005-2013 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/Sinks/AESinkDARWINIOS.h"
22 #include "cores/AudioEngine/Utils/AEUtil.h"
23 #include "cores/AudioEngine/Utils/AERingBuffer.h"
24 #include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
25 #include "osx/DarwinUtils.h"
26 #include "utils/log.h"
27 #include "utils/StringUtils.h"
28 #include "threads/Condition.h"
29 #include "windowing/WindowingFactory.h"
30
31 #include <sstream>
32 #include <AudioToolbox/AudioToolbox.h>
33
34 #define CA_MAX_CHANNELS 8
35 static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = {
36   AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
37   AE_CH_NULL
38 };
39
40 /***************************************************************************************/
41 /***************************************************************************************/
42 #if DO_440HZ_TONE_TEST
43 static void SineWaveGeneratorInitWithFrequency(SineWaveGenerator *ctx, double frequency, double samplerate)
44 {
45   // Given:
46   //   frequency in cycles per second
47   //   2*PI radians per sine wave cycle
48   //   sample rate in samples per second
49   //
50   // Then:
51   //   cycles     radians     seconds     radians
52   //   ------  *  -------  *  -------  =  -------
53   //   second      cycle      sample      sample
54   ctx->currentPhase = 0.0;
55   ctx->phaseIncrement = frequency * 2*M_PI / samplerate;
56 }
57
58 static int16_t SineWaveGeneratorNextSampleInt16(SineWaveGenerator *ctx)
59 {
60   int16_t sample = INT16_MAX * sinf(ctx->currentPhase);
61
62   ctx->currentPhase += ctx->phaseIncrement;
63   // Keep the value between 0 and 2*M_PI
64   while (ctx->currentPhase > 2*M_PI)
65     ctx->currentPhase -= 2*M_PI;
66
67   return sample / 4;
68 }
69 static float SineWaveGeneratorNextSampleFloat(SineWaveGenerator *ctx)
70 {
71   float sample = MAXFLOAT * sinf(ctx->currentPhase);
72   
73   ctx->currentPhase += ctx->phaseIncrement;
74   // Keep the value between 0 and 2*M_PI
75   while (ctx->currentPhase > 2*M_PI)
76     ctx->currentPhase -= 2*M_PI;
77   
78   return sample / 4;
79 }
80 #endif
81
82 /***************************************************************************************/
83 /***************************************************************************************/
84 class CAAudioUnitSink
85 {
86   public:
87     CAAudioUnitSink();
88    ~CAAudioUnitSink();
89
90     bool         open(AudioStreamBasicDescription outputFormat);
91     bool         close();
92     bool         play(bool mute);
93     bool         mute(bool mute);
94     bool         pause();
95     void         drain();
96     double       getDelay();
97     double       cacheSize();
98     unsigned int write(uint8_t *data, unsigned int byte_count);
99     unsigned int chunkSize() { return m_bufferDuration * m_sampleRate; }
100     unsigned int getRealisedSampleRate() { return m_outputFormat.mSampleRate; }
101     static Float64 getCoreAudioRealisedSampleRate();
102
103   private:
104     void         setCoreAudioBuffersize();
105     bool         setCoreAudioInputFormat();
106     void         setCoreAudioPreferredSampleRate();
107     bool         setupAudio();
108     bool         checkAudioRoute();
109     bool         checkSessionProperties();
110     bool         activateAudioSession();
111     void         deactivateAudioSession();
112  
113     // callbacks
114     static void sessionPropertyCallback(void *inClientData,
115                   AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData);
116
117     static void sessionInterruptionCallback(void *inClientData, UInt32 inInterruption);
118
119     static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
120                   const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames,
121                   AudioBufferList *ioData);
122
123     bool                m_setup;
124     bool                m_initialized;
125     bool                m_activated;
126     AudioUnit           m_audioUnit;
127     AudioStreamBasicDescription m_outputFormat;
128     AERingBuffer       *m_buffer;
129
130     bool                m_mute;
131     Float32             m_outputVolume;
132     Float32             m_outputLatency;
133     Float32             m_bufferDuration;
134
135     unsigned int        m_sampleRate;
136     unsigned int        m_frameSize;
137     unsigned int        m_frames;
138
139     bool                m_playing;
140     bool                m_playing_saved;
141     volatile bool       m_started;
142 };
143
144 CAAudioUnitSink::CAAudioUnitSink()
145 : m_initialized(false)
146 , m_activated(false)
147 , m_buffer(NULL)
148 , m_playing(false)
149 , m_playing_saved(false)
150 , m_started(false)
151 {
152 }
153
154 CAAudioUnitSink::~CAAudioUnitSink()
155 {
156   close();
157 }
158
159 bool CAAudioUnitSink::open(AudioStreamBasicDescription outputFormat)
160 {
161   m_mute          = false;
162   m_setup         = false;
163   m_outputFormat  = outputFormat;
164   m_outputLatency = 0.0;
165   m_bufferDuration= 0.0;
166   m_outputVolume  = 1.0;
167   m_sampleRate    = (unsigned int)outputFormat.mSampleRate;
168   m_frameSize     = outputFormat.mChannelsPerFrame * outputFormat.mBitsPerChannel / 8;
169
170   /* TODO: Reduce the size of this buffer, pre-calculate the size based on how large
171            the buffers are that CA calls us with in the renderCallback - perhaps call
172            the checkSessionProperties() before running this? */
173   m_buffer = new AERingBuffer(16384);
174
175   return setupAudio();
176 }
177
178 bool CAAudioUnitSink::close()
179 {
180   deactivateAudioSession();
181   
182   delete m_buffer;
183   m_buffer = NULL;
184
185   m_started = false;
186   return true;
187 }
188
189 bool CAAudioUnitSink::play(bool mute)
190 {    
191   if (!m_playing)
192   {
193     if (activateAudioSession())
194     {
195       CAAudioUnitSink::mute(mute);
196       m_playing = !AudioOutputUnitStart(m_audioUnit);
197     }
198   }
199
200   return m_playing;
201 }
202
203 bool CAAudioUnitSink::mute(bool mute)
204 {
205   m_mute = mute;
206
207   return true;
208 }
209
210 bool CAAudioUnitSink::pause()
211 {       
212   if (m_playing)
213     m_playing = AudioOutputUnitStop(m_audioUnit);
214
215   return m_playing;
216 }
217
218 double CAAudioUnitSink::getDelay()
219 {
220   double delay = (double)m_buffer->GetReadSize() / m_frameSize;
221   delay /= m_sampleRate;
222   delay += m_bufferDuration + m_outputLatency;
223
224   return delay;
225 }
226
227 double CAAudioUnitSink::cacheSize()
228 {
229   return (double)m_buffer->GetMaxSize() / (double)(m_frameSize * m_sampleRate);
230 }
231
232 CCriticalSection mutex;
233 XbmcThreads::ConditionVariable condVar;
234
235 unsigned int CAAudioUnitSink::write(uint8_t *data, unsigned int frames)
236 {
237   if (m_buffer->GetWriteSize() < frames * m_frameSize)
238   { // no space to write - wait for a bit
239     CSingleLock lock(mutex);
240     if (!m_started)
241       condVar.wait(lock);
242     else
243       condVar.wait(lock, 900 * frames / m_sampleRate);
244   }
245
246   unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_frameSize);
247   if (write_frames)
248     m_buffer->Write(data, write_frames * m_frameSize);
249   
250   return write_frames;
251 }
252
253 void CAAudioUnitSink::drain()
254 {
255   unsigned int bytes = m_buffer->GetReadSize();
256   while (bytes)
257   {
258     CSingleLock lock(mutex);
259     condVar.wait(mutex, 900 * bytes / (m_sampleRate * m_frameSize));
260     bytes = m_buffer->GetReadSize();
261   }
262 }
263
264 void CAAudioUnitSink::setCoreAudioBuffersize()
265 {
266 #if !TARGET_IPHONE_SIMULATOR
267   OSStatus status = noErr;
268   // set the buffer size, this affects the number of samples
269   // that get rendered every time the audio callback is fired.
270   Float32 preferredBufferSize = 512 * m_outputFormat.mChannelsPerFrame / m_outputFormat.mSampleRate;
271   CLog::Log(LOGNOTICE, "%s setting buffer duration to %f", __PRETTY_FUNCTION__, preferredBufferSize);
272   status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
273                                    sizeof(preferredBufferSize), &preferredBufferSize);
274   if (status != noErr)
275     CLog::Log(LOGWARNING, "%s preferredBufferSize couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
276 #endif
277 }
278
279 bool CAAudioUnitSink::setCoreAudioInputFormat()
280 {
281   // Set the output stream format
282   UInt32 ioDataSize = sizeof(AudioStreamBasicDescription);
283   OSStatus status = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat,
284                                 kAudioUnitScope_Input, 0, &m_outputFormat, ioDataSize);
285   if (status != noErr)
286   {
287     CLog::Log(LOGERROR, "%s error setting stream format on audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
288     return false;
289   }
290   return true;
291 }
292
293 void CAAudioUnitSink::setCoreAudioPreferredSampleRate()
294 {
295   Float64 preferredSampleRate = m_outputFormat.mSampleRate;
296   CLog::Log(LOGNOTICE, "%s requesting hw samplerate %f", __PRETTY_FUNCTION__, preferredSampleRate);
297   OSStatus status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate,
298                                    sizeof(preferredSampleRate), &preferredSampleRate);
299   if (status != noErr)
300     CLog::Log(LOGWARNING, "%s preferredSampleRate couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
301 }
302
303 Float64 CAAudioUnitSink::getCoreAudioRealisedSampleRate()
304 {
305   Float64 outputSampleRate = 0.0;
306   UInt32 ioDataSize = sizeof(outputSampleRate);
307   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
308                               &ioDataSize, &outputSampleRate) != noErr)
309     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareSampleRate", __FUNCTION__);
310   return outputSampleRate;
311 }
312
313 bool CAAudioUnitSink::setupAudio()
314 {
315   OSStatus status = noErr;
316   if (m_setup && m_audioUnit)
317     return true;
318
319   // Audio Session Setup
320   UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
321   status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
322                                    sizeof(sessionCategory), &sessionCategory);
323   if (status != noErr)
324   {
325     CLog::Log(LOGERROR, "%s error setting sessioncategory (error: %d)", __PRETTY_FUNCTION__, (int)status);
326     return false;
327   }
328
329   AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
330     sessionPropertyCallback, this);
331
332   AudioSessionAddPropertyListener(kAudioSessionProperty_CurrentHardwareOutputVolume,
333     sessionPropertyCallback, this);
334  
335   if (AudioSessionSetActive(true) != noErr)
336     return false;
337
338   // Audio Unit Setup
339   // Describe a default output unit.
340   AudioComponentDescription description = {};
341   description.componentType = kAudioUnitType_Output;
342   description.componentSubType = kAudioUnitSubType_RemoteIO;
343   description.componentManufacturer = kAudioUnitManufacturer_Apple;
344
345   // Get component
346   AudioComponent component;
347   component = AudioComponentFindNext(NULL, &description);
348   status = AudioComponentInstanceNew(component, &m_audioUnit);
349   if (status != noErr)
350   {
351     CLog::Log(LOGERROR, "%s error creating audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
352     return false;
353   }
354   
355   setCoreAudioPreferredSampleRate();
356  
357         // Get the output samplerate for knowing what was setup in reality
358   Float64 realisedSampleRate = getCoreAudioRealisedSampleRate();
359   if (m_outputFormat.mSampleRate != realisedSampleRate)
360   {
361     CLog::Log(LOGNOTICE, "%s couldn't set requested samplerate %d, coreaudio will resample to %d instead", __PRETTY_FUNCTION__, (int)m_outputFormat.mSampleRate, (int)realisedSampleRate);
362     // if we don't ca to resample - but instead let activeae resample -
363     // reflect the realised samplerate to the outputformat here
364     // well maybe it is handy in the future - as of writing this
365     // ca was about 6 times faster then activeae ;)
366     //m_outputFormat.mSampleRate = realisedSampleRate;
367     //m_sampleRate = realisedSampleRate;
368   }
369
370   setCoreAudioBuffersize();
371   if (!setCoreAudioInputFormat())
372     return false;
373
374   // Attach a render callback on the unit
375   AURenderCallbackStruct callbackStruct = {};
376   callbackStruct.inputProc = renderCallback;
377   callbackStruct.inputProcRefCon = this;
378   status = AudioUnitSetProperty(m_audioUnit,
379                                 kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
380                                 0, &callbackStruct, sizeof(callbackStruct));
381   if (status != noErr)
382   {
383     CLog::Log(LOGERROR, "%s error setting render callback for audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
384     return false;
385   }
386
387   status = AudioUnitInitialize(m_audioUnit);
388         if (status != noErr)
389   {
390     CLog::Log(LOGERROR, "%s error initializing audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
391     return false;
392   }
393
394   checkSessionProperties();
395
396   m_setup = true;
397   std::string formatString;
398   CLog::Log(LOGNOTICE, "%s setup audio format: %s", __PRETTY_FUNCTION__, StreamDescriptionToString(m_outputFormat, formatString));
399
400   return m_setup;
401 }
402
403 bool CAAudioUnitSink::checkAudioRoute()
404 {
405   // why do we need to know the audio route ?
406   CFStringRef route;
407   UInt32 propertySize = sizeof(CFStringRef);
408   if (AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route) != noErr)
409     return false;
410
411   return true;
412 }
413
414 bool CAAudioUnitSink::checkSessionProperties()
415 {
416   checkAudioRoute();
417
418   UInt32 ioDataSize;
419   ioDataSize = sizeof(m_outputVolume);
420   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume,
421     &ioDataSize, &m_outputVolume) != noErr)
422     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputVolume", __FUNCTION__);
423
424   ioDataSize = sizeof(m_outputLatency);
425   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
426     &ioDataSize, &m_outputLatency) != noErr)
427     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputLatency", __FUNCTION__);
428
429   ioDataSize = sizeof(m_bufferDuration);
430   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
431     &ioDataSize, &m_bufferDuration) != noErr)
432     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareIOBufferDuration", __FUNCTION__);
433    
434   CLog::Log(LOGDEBUG, "%s: volume = %f, latency = %f, buffer = %f", __FUNCTION__, m_outputVolume, m_outputLatency, m_bufferDuration);
435   return true;
436 }
437
438 bool CAAudioUnitSink::activateAudioSession()
439 {
440   if (!m_activated)
441   {
442     if (!m_initialized)
443     {
444       OSStatus osstat = AudioSessionInitialize(NULL, kCFRunLoopDefaultMode, sessionInterruptionCallback, this);
445       if (osstat == kAudioSessionNoError || osstat == kAudioSessionAlreadyInitialized)
446         m_initialized = true;
447       else
448       {
449         CLog::Log(LOGERROR, "%s error initializing audio session (error: %d)", __PRETTY_FUNCTION__, (int)osstat);
450         return false;
451       }
452     }
453     if (checkAudioRoute() && setupAudio())
454       m_activated = true;
455   }
456
457   return m_activated;
458 }
459
460 void CAAudioUnitSink::deactivateAudioSession()
461 {
462   if (m_activated)
463   {
464     pause();
465     AudioUnitUninitialize(m_audioUnit);
466     AudioComponentInstanceDispose(m_audioUnit), m_audioUnit = NULL;
467     AudioSessionSetActive(false);
468     AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange,
469       sessionPropertyCallback, this);
470     AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_CurrentHardwareOutputVolume,
471       sessionPropertyCallback, this);
472
473     m_setup = false;
474     m_activated = false;
475   }
476 }
477
478 void CAAudioUnitSink::sessionPropertyCallback(void *inClientData,
479   AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData)
480 {
481   CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
482
483   if (inID == kAudioSessionProperty_AudioRouteChange)
484   {
485     if (sink->checkAudioRoute())
486       sink->checkSessionProperties();
487   }
488   else if (inID == kAudioSessionProperty_CurrentHardwareOutputVolume)
489   {
490     if (inData && inDataSize == 4)
491       sink->m_outputVolume = *(float*)inData;
492   }
493 }
494
495 void CAAudioUnitSink::sessionInterruptionCallback(void *inClientData, UInt32 inInterruption)
496 {    
497   CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
498
499   if (inInterruption == kAudioSessionBeginInterruption)
500   {
501     CLog::Log(LOGDEBUG, "Bgn interuption");
502     sink->m_playing_saved = sink->m_playing;
503     sink->pause();
504   }
505   else if (inInterruption == kAudioSessionEndInterruption)
506   {
507     CLog::Log(LOGDEBUG, "End interuption");
508     if (sink->m_playing_saved)
509     {
510       sink->m_playing_saved = false;
511       sink->play(sink->m_mute);
512     }
513   }
514 }
515
516 OSStatus CAAudioUnitSink::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
517   const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
518 {
519   CAAudioUnitSink *sink = (CAAudioUnitSink*)inRefCon;
520
521   sink->m_started = true;
522
523   for (unsigned int i = 0; i < ioData->mNumberBuffers; i++)
524   {
525     // buffers come from CA already zero'd, so just copy what is wanted
526     unsigned int wanted = ioData->mBuffers[i].mDataByteSize;
527     unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
528     sink->m_buffer->Read((unsigned char*)ioData->mBuffers[i].mData, bytes);
529     if (bytes != wanted)
530       CLog::Log(LOGERROR, "%s: %sFLOW (%i vs %i) bytes", __FUNCTION__, bytes > wanted ? "OVER" : "UNDER", bytes, wanted);
531     if (bytes == 0)
532       *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
533   }
534   // tell the sink we're good for more data
535   condVar.notifyAll();
536
537   return noErr;
538 }
539
540 /***************************************************************************************/
541 /***************************************************************************************/
542 static void EnumerateDevices(AEDeviceInfoList &list)
543 {
544   CAEDeviceInfo device;
545
546   device.m_deviceName = "default";
547   device.m_displayName = "Default";
548   device.m_displayNameExtra = "";
549 #if defined(TARGET_DARWIN_IOS_ATV2)
550   device.m_deviceType = AE_DEVTYPE_IEC958;
551   device.m_dataFormats.push_back(AE_FMT_AC3);
552   device.m_dataFormats.push_back(AE_FMT_DTS);
553 #else
554   // TODO screen changing on ios needs to call
555   // devices changed once this is available in activae
556   if (g_Windowing.GetCurrentScreen() > 0)
557   {
558     device.m_deviceType = AE_DEVTYPE_IEC958; //allow passthrough for tvout
559     device.m_dataFormats.push_back(AE_FMT_AC3);
560     device.m_dataFormats.push_back(AE_FMT_DTS);
561   }
562   else
563     device.m_deviceType = AE_DEVTYPE_PCM;
564 #endif
565
566   // add channel info
567   CAEChannelInfo channel_info;
568   for (UInt32 chan = 0; chan < 2; ++chan)
569   {
570     if (!device.m_channels.HasChannel(CAChannelMap[chan]))
571       device.m_channels += CAChannelMap[chan];
572     channel_info += CAChannelMap[chan];
573   }
574
575   // there are more supported ( one of those 2 gets resampled
576   // by coreaudio anyway) - but for keeping it save ignore
577   // the others...
578   device.m_sampleRates.push_back(44100);
579   device.m_sampleRates.push_back(48000);
580
581   device.m_dataFormats.push_back(AE_FMT_S16LE);
582   //device.m_dataFormats.push_back(AE_FMT_S24LE3);
583   //device.m_dataFormats.push_back(AE_FMT_S32LE);
584   // AE_FMT_FLOAT is 3% slower on atv2
585   // then S16LE - so leave it out for now
586   //device.m_dataFormats.push_back(AE_FMT_FLOAT);
587
588   CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
589
590   list.push_back(device);
591 }
592
593 /***************************************************************************************/
594 /***************************************************************************************/
595 AEDeviceInfoList CAESinkDARWINIOS::m_devices;
596
597 CAESinkDARWINIOS::CAESinkDARWINIOS()
598 :   m_audioSink(NULL)
599 {
600 }
601
602 CAESinkDARWINIOS::~CAESinkDARWINIOS()
603 {
604 }
605
606 bool CAESinkDARWINIOS::Initialize(AEAudioFormat &format, std::string &device)
607 {
608   bool found = false;
609   bool forceRaw = false;
610
611   std::string devicelower = device;
612   StringUtils::ToLower(devicelower);
613   for (size_t i = 0; i < m_devices.size(); i++)
614   {
615     if (devicelower.find(m_devices[i].m_deviceName) != std::string::npos)
616     {
617       m_info = m_devices[i];
618       found = true;
619       break;
620     }
621   }
622   
623   if (!found)
624     return false;
625
626   AudioStreamBasicDescription audioFormat = {};
627
628   // AE_FMT_FLOAT is 3% slower on atv2
629   // then S16LE - so leave it out for now
630   // just leave the code commented in here
631   // as it might come handy at some point maybe ...
632   //if (format.m_dataFormat == AE_FMT_FLOAT)
633   //  audioFormat.mFormatFlags    |= kLinearPCMFormatFlagIsFloat;
634   //else// this will be selected when AE wants AC3 or DTS or anything other then float
635   {
636     audioFormat.mFormatFlags    |= kLinearPCMFormatFlagIsSignedInteger;
637     if (AE_IS_RAW(format.m_dataFormat))
638       forceRaw = true;
639     format.m_dataFormat = AE_FMT_S16LE;
640   }
641
642   format.m_channelLayout = m_info.m_channels;
643   format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
644
645   
646   audioFormat.mFormatID = kAudioFormatLinearPCM;
647   switch(format.m_sampleRate)
648   {
649     case 11025:
650     case 22050:
651     case 44100:
652     case 88200:
653     case 176400:
654       audioFormat.mSampleRate = 44100;
655       break;
656     default:
657     case 8000:
658     case 12000:
659     case 16000:
660     case 24000:
661     case 32000:
662     case 48000:
663     case 96000:
664     case 192000:
665     case 384000:
666       audioFormat.mSampleRate = 48000;
667       break;
668   }
669   
670   if (forceRaw)//make sure input and output samplerate match for preventing resampling
671     audioFormat.mSampleRate = CAAudioUnitSink::getCoreAudioRealisedSampleRate();
672   
673   audioFormat.mFramesPerPacket = 1;
674   audioFormat.mChannelsPerFrame= 2;// ios only supports 2 channels
675   audioFormat.mBitsPerChannel  = CAEUtil::DataFormatToBits(format.m_dataFormat);
676   audioFormat.mBytesPerFrame   = format.m_frameSize;
677   audioFormat.mBytesPerPacket  = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
678   audioFormat.mFormatFlags    |= kLinearPCMFormatFlagIsPacked;
679   
680 #if DO_440HZ_TONE_TEST
681   SineWaveGeneratorInitWithFrequency(&m_SineWaveGenerator, 440.0, audioFormat.mSampleRate);
682 #endif
683
684   m_audioSink = new CAAudioUnitSink;
685   m_audioSink->open(audioFormat);
686
687   format.m_frames = m_audioSink->chunkSize();
688   format.m_frameSamples = format.m_frames * audioFormat.mChannelsPerFrame;
689   // reset to the realised samplerate
690   format.m_sampleRate = m_audioSink->getRealisedSampleRate();
691   m_format = format;
692
693   m_volume_changed = false;
694   m_audioSink->play(false);
695
696   return true;
697 }
698
699 void CAESinkDARWINIOS::Deinitialize()
700 {
701   delete m_audioSink;
702   m_audioSink = NULL;
703 }
704
705 bool CAESinkDARWINIOS::IsCompatible(const AEAudioFormat &format, const std::string &device)
706 {
707   return ((m_format.m_sampleRate    == format.m_sampleRate) &&
708           (m_format.m_dataFormat    == format.m_dataFormat) &&
709           (m_format.m_channelLayout == format.m_channelLayout));
710 }
711
712 double CAESinkDARWINIOS::GetDelay()
713 {
714   if (m_audioSink)
715     return m_audioSink->getDelay();
716   return 0.0;
717 }
718
719 double CAESinkDARWINIOS::GetCacheTotal()
720 {
721   if (m_audioSink)
722     return m_audioSink->cacheSize();
723   return 0.0;
724 }
725
726 unsigned int CAESinkDARWINIOS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
727 {
728   
729 #if DO_440HZ_TONE_TEST
730   if (m_format.m_dataFormat == AE_FMT_FLOAT)
731   {
732     float *samples = (float*)data;
733     for (unsigned int j = 0; j < frames ; j++)
734     {
735       float sample = SineWaveGeneratorNextSampleFloat(&m_SineWaveGenerator);
736       *samples++ = sample;
737       *samples++ = sample;
738     }
739     
740   }
741   else
742   {
743     int16_t *samples = (int16_t*)data;
744     for (unsigned int j = 0; j < frames ; j++)
745     {
746       int16_t sample = SineWaveGeneratorNextSampleInt16(&m_SineWaveGenerator);
747       *samples++ = sample;
748       *samples++ = sample;
749     }
750   }
751 #endif
752   if (m_audioSink)
753     return m_audioSink->write(data, frames);
754   return 0;
755 }
756
757 void CAESinkDARWINIOS::Drain()
758 {
759   if (m_audioSink)
760     m_audioSink->drain();
761 }
762
763 bool CAESinkDARWINIOS::HasVolume()
764 {
765   return false;
766 }
767
768 void  CAESinkDARWINIOS::SetVolume(float scale)
769 {
770   // CoreAudio uses fixed steps, reverse scale back to percent
771   float gain = CAEUtil::ScaleToGain(scale);
772   m_volume = CAEUtil::GainToPercent(gain);
773   m_volume_changed = true;
774 }
775
776 void CAESinkDARWINIOS::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
777 {
778   m_devices.clear();
779   EnumerateDevices(m_devices);
780   list = m_devices;
781 }