Merge pull request #4196 from arnova/sub_fallback
[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   CCriticalSection mutex;
256   unsigned int bytes = m_buffer->GetReadSize();
257   while (bytes)
258   {
259     CSingleLock lock(mutex);
260     condVar.wait(mutex, 900 * bytes / (m_sampleRate * m_frameSize));
261     bytes = m_buffer->GetReadSize();
262   }
263 }
264
265 void CAAudioUnitSink::setCoreAudioBuffersize()
266 {
267 #if !TARGET_IPHONE_SIMULATOR
268   OSStatus status = noErr;
269   // set the buffer size, this affects the number of samples
270   // that get rendered every time the audio callback is fired.
271   Float32 preferredBufferSize = 512 * m_outputFormat.mChannelsPerFrame / m_outputFormat.mSampleRate;
272   CLog::Log(LOGNOTICE, "%s setting buffer duration to %f", __PRETTY_FUNCTION__, preferredBufferSize);
273   status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
274                                    sizeof(preferredBufferSize), &preferredBufferSize);
275   if (status != noErr)
276     CLog::Log(LOGWARNING, "%s preferredBufferSize couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
277 #endif
278 }
279
280 bool CAAudioUnitSink::setCoreAudioInputFormat()
281 {
282   // Set the output stream format
283   UInt32 ioDataSize = sizeof(AudioStreamBasicDescription);
284   OSStatus status = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat,
285                                 kAudioUnitScope_Input, 0, &m_outputFormat, ioDataSize);
286   if (status != noErr)
287   {
288     CLog::Log(LOGERROR, "%s error setting stream format on audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
289     return false;
290   }
291   return true;
292 }
293
294 void CAAudioUnitSink::setCoreAudioPreferredSampleRate()
295 {
296   Float64 preferredSampleRate = m_outputFormat.mSampleRate;
297   CLog::Log(LOGNOTICE, "%s requesting hw samplerate %f", __PRETTY_FUNCTION__, preferredSampleRate);
298   OSStatus status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate,
299                                    sizeof(preferredSampleRate), &preferredSampleRate);
300   if (status != noErr)
301     CLog::Log(LOGWARNING, "%s preferredSampleRate couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
302 }
303
304 Float64 CAAudioUnitSink::getCoreAudioRealisedSampleRate()
305 {
306   Float64 outputSampleRate = 0.0;
307   UInt32 ioDataSize = sizeof(outputSampleRate);
308   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
309                               &ioDataSize, &outputSampleRate) != noErr)
310     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareSampleRate", __FUNCTION__);
311   return outputSampleRate;
312 }
313
314 bool CAAudioUnitSink::setupAudio()
315 {
316   OSStatus status = noErr;
317   if (m_setup && m_audioUnit)
318     return true;
319
320   // Audio Session Setup
321   UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
322   status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
323                                    sizeof(sessionCategory), &sessionCategory);
324   if (status != noErr)
325   {
326     CLog::Log(LOGERROR, "%s error setting sessioncategory (error: %d)", __PRETTY_FUNCTION__, (int)status);
327     return false;
328   }
329
330   AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
331     sessionPropertyCallback, this);
332
333   AudioSessionAddPropertyListener(kAudioSessionProperty_CurrentHardwareOutputVolume,
334     sessionPropertyCallback, this);
335  
336   if (AudioSessionSetActive(true) != noErr)
337     return false;
338
339   // Audio Unit Setup
340   // Describe a default output unit.
341   AudioComponentDescription description = {};
342   description.componentType = kAudioUnitType_Output;
343   description.componentSubType = kAudioUnitSubType_RemoteIO;
344   description.componentManufacturer = kAudioUnitManufacturer_Apple;
345
346   // Get component
347   AudioComponent component;
348   component = AudioComponentFindNext(NULL, &description);
349   status = AudioComponentInstanceNew(component, &m_audioUnit);
350   if (status != noErr)
351   {
352     CLog::Log(LOGERROR, "%s error creating audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
353     return false;
354   }
355   
356   setCoreAudioPreferredSampleRate();
357  
358         // Get the output samplerate for knowing what was setup in reality
359   Float64 realisedSampleRate = getCoreAudioRealisedSampleRate();
360   if (m_outputFormat.mSampleRate != realisedSampleRate)
361   {
362     CLog::Log(LOGNOTICE, "%s couldn't set requested samplerate %d, coreaudio will resample to %d instead", __PRETTY_FUNCTION__, (int)m_outputFormat.mSampleRate, (int)realisedSampleRate);
363     // if we don't ca to resample - but instead let activeae resample -
364     // reflect the realised samplerate to the outputformat here
365     // well maybe it is handy in the future - as of writing this
366     // ca was about 6 times faster then activeae ;)
367     //m_outputFormat.mSampleRate = realisedSampleRate;
368     //m_sampleRate = realisedSampleRate;
369   }
370
371   setCoreAudioBuffersize();
372   if (!setCoreAudioInputFormat())
373     return false;
374
375   // Attach a render callback on the unit
376   AURenderCallbackStruct callbackStruct = {};
377   callbackStruct.inputProc = renderCallback;
378   callbackStruct.inputProcRefCon = this;
379   status = AudioUnitSetProperty(m_audioUnit,
380                                 kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
381                                 0, &callbackStruct, sizeof(callbackStruct));
382   if (status != noErr)
383   {
384     CLog::Log(LOGERROR, "%s error setting render callback for audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
385     return false;
386   }
387
388   status = AudioUnitInitialize(m_audioUnit);
389         if (status != noErr)
390   {
391     CLog::Log(LOGERROR, "%s error initializing audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
392     return false;
393   }
394
395   checkSessionProperties();
396
397   m_setup = true;
398   std::string formatString;
399   CLog::Log(LOGNOTICE, "%s setup audio format: %s", __PRETTY_FUNCTION__, StreamDescriptionToString(m_outputFormat, formatString));
400
401   return m_setup;
402 }
403
404 bool CAAudioUnitSink::checkAudioRoute()
405 {
406   // why do we need to know the audio route ?
407   CFStringRef route;
408   UInt32 propertySize = sizeof(CFStringRef);
409   if (AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route) != noErr)
410     return false;
411
412   return true;
413 }
414
415 bool CAAudioUnitSink::checkSessionProperties()
416 {
417   checkAudioRoute();
418
419   UInt32 ioDataSize;
420   ioDataSize = sizeof(m_outputVolume);
421   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume,
422     &ioDataSize, &m_outputVolume) != noErr)
423     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputVolume", __FUNCTION__);
424
425   ioDataSize = sizeof(m_outputLatency);
426   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
427     &ioDataSize, &m_outputLatency) != noErr)
428     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputLatency", __FUNCTION__);
429
430   ioDataSize = sizeof(m_bufferDuration);
431   if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
432     &ioDataSize, &m_bufferDuration) != noErr)
433     CLog::Log(LOGERROR, "%s: error getting CurrentHardwareIOBufferDuration", __FUNCTION__);
434    
435   CLog::Log(LOGDEBUG, "%s: volume = %f, latency = %f, buffer = %f", __FUNCTION__, m_outputVolume, m_outputLatency, m_bufferDuration);
436   return true;
437 }
438
439 bool CAAudioUnitSink::activateAudioSession()
440 {
441   if (!m_activated)
442   {
443     if (!m_initialized)
444     {
445       OSStatus osstat = AudioSessionInitialize(NULL, kCFRunLoopDefaultMode, sessionInterruptionCallback, this);
446       if (osstat == kAudioSessionNoError || osstat == kAudioSessionAlreadyInitialized)
447         m_initialized = true;
448       else
449       {
450         CLog::Log(LOGERROR, "%s error initializing audio session (error: %d)", __PRETTY_FUNCTION__, (int)osstat);
451         return false;
452       }
453     }
454     if (checkAudioRoute() && setupAudio())
455       m_activated = true;
456   }
457
458   return m_activated;
459 }
460
461 void CAAudioUnitSink::deactivateAudioSession()
462 {
463   if (m_activated)
464   {
465     pause();
466     AudioUnitUninitialize(m_audioUnit);
467     AudioComponentInstanceDispose(m_audioUnit), m_audioUnit = NULL;
468     AudioSessionSetActive(false);
469     AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange,
470       sessionPropertyCallback, this);
471     AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_CurrentHardwareOutputVolume,
472       sessionPropertyCallback, this);
473
474     m_setup = false;
475     m_activated = false;
476   }
477 }
478
479 void CAAudioUnitSink::sessionPropertyCallback(void *inClientData,
480   AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData)
481 {
482   CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
483
484   if (inID == kAudioSessionProperty_AudioRouteChange)
485   {
486     if (sink->checkAudioRoute())
487       sink->checkSessionProperties();
488   }
489   else if (inID == kAudioSessionProperty_CurrentHardwareOutputVolume)
490   {
491     if (inData && inDataSize == 4)
492       sink->m_outputVolume = *(float*)inData;
493   }
494 }
495
496 void CAAudioUnitSink::sessionInterruptionCallback(void *inClientData, UInt32 inInterruption)
497 {    
498   CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
499
500   if (inInterruption == kAudioSessionBeginInterruption)
501   {
502     CLog::Log(LOGDEBUG, "Bgn interuption");
503     sink->m_playing_saved = sink->m_playing;
504     sink->pause();
505   }
506   else if (inInterruption == kAudioSessionEndInterruption)
507   {
508     CLog::Log(LOGDEBUG, "End interuption");
509     if (sink->m_playing_saved)
510     {
511       sink->m_playing_saved = false;
512       sink->play(sink->m_mute);
513     }
514   }
515 }
516
517 OSStatus CAAudioUnitSink::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
518   const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
519 {
520   CAAudioUnitSink *sink = (CAAudioUnitSink*)inRefCon;
521
522   sink->m_started = true;
523
524   for (unsigned int i = 0; i < ioData->mNumberBuffers; i++)
525   {
526     // buffers come from CA already zero'd, so just copy what is wanted
527     unsigned int wanted = ioData->mBuffers[i].mDataByteSize;
528     unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
529     sink->m_buffer->Read((unsigned char*)ioData->mBuffers[i].mData, bytes);
530     if (bytes != wanted)
531       CLog::Log(LOGERROR, "%s: %sFLOW (%i vs %i) bytes", __FUNCTION__, bytes > wanted ? "OVER" : "UNDER", bytes, wanted);
532   }
533   // tell the sink we're good for more data
534   condVar.notifyAll();
535
536   return noErr;
537 }
538
539 /***************************************************************************************/
540 /***************************************************************************************/
541 static void EnumerateDevices(AEDeviceInfoList &list)
542 {
543   CAEDeviceInfo device;
544
545   device.m_deviceName = "default";
546   device.m_displayName = "Default";
547   device.m_displayNameExtra = "";
548 #if defined(TARGET_DARWIN_IOS_ATV2)
549   device.m_deviceType = AE_DEVTYPE_IEC958;
550   device.m_dataFormats.push_back(AE_FMT_AC3);
551   device.m_dataFormats.push_back(AE_FMT_DTS);
552 #else
553   // TODO screen changing on ios needs to call
554   // devices changed once this is available in activae
555   if (g_Windowing.GetCurrentScreen() > 0)
556   {
557     device.m_deviceType = AE_DEVTYPE_IEC958; //allow passthrough for tvout
558     device.m_dataFormats.push_back(AE_FMT_AC3);
559     device.m_dataFormats.push_back(AE_FMT_DTS);
560   }
561   else
562     device.m_deviceType = AE_DEVTYPE_PCM;
563 #endif
564
565   // add channel info
566   CAEChannelInfo channel_info;
567   for (UInt32 chan = 0; chan < 2; ++chan)
568   {
569     if (!device.m_channels.HasChannel(CAChannelMap[chan]))
570       device.m_channels += CAChannelMap[chan];
571     channel_info += CAChannelMap[chan];
572   }
573
574   // there are more supported ( one of those 2 gets resampled
575   // by coreaudio anyway) - but for keeping it save ignore
576   // the others...
577   device.m_sampleRates.push_back(44100);
578   device.m_sampleRates.push_back(48000);
579
580   device.m_dataFormats.push_back(AE_FMT_S16LE);
581   //device.m_dataFormats.push_back(AE_FMT_S24LE3);
582   //device.m_dataFormats.push_back(AE_FMT_S32LE);
583   // AE_FMT_FLOAT is 3% slower on atv2
584   // then S16LE - so leave it out for now
585   //device.m_dataFormats.push_back(AE_FMT_FLOAT);
586
587   CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
588
589   list.push_back(device);
590 }
591
592 /***************************************************************************************/
593 /***************************************************************************************/
594 AEDeviceInfoList CAESinkDARWINIOS::m_devices;
595
596 CAESinkDARWINIOS::CAESinkDARWINIOS()
597 :   m_audioSink(NULL)
598 {
599 }
600
601 CAESinkDARWINIOS::~CAESinkDARWINIOS()
602 {
603 }
604
605 bool CAESinkDARWINIOS::Initialize(AEAudioFormat &format, std::string &device)
606 {
607   bool found = false;
608   bool forceRaw = false;
609
610   std::string devicelower = device;
611   StringUtils::ToLower(devicelower);
612   for (size_t i = 0; i < m_devices.size(); i++)
613   {
614     if (devicelower.find(m_devices[i].m_deviceName) != std::string::npos)
615     {
616       m_info = m_devices[i];
617       found = true;
618       break;
619     }
620   }
621   
622   if (!found)
623     return false;
624
625   AudioStreamBasicDescription audioFormat = {};
626
627   // AE_FMT_FLOAT is 3% slower on atv2
628   // then S16LE - so leave it out for now
629   // just leave the code commented in here
630   // as it might come handy at some point maybe ...
631   //if (format.m_dataFormat == AE_FMT_FLOAT)
632   //  audioFormat.mFormatFlags    |= kLinearPCMFormatFlagIsFloat;
633   //else// this will be selected when AE wants AC3 or DTS or anything other then float
634   {
635     audioFormat.mFormatFlags    |= kLinearPCMFormatFlagIsSignedInteger;
636     if (AE_IS_RAW(format.m_dataFormat))
637       forceRaw = true;
638     format.m_dataFormat = AE_FMT_S16LE;
639   }
640
641   format.m_channelLayout = m_info.m_channels;
642   format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
643
644   
645   audioFormat.mFormatID = kAudioFormatLinearPCM;
646   switch(format.m_sampleRate)
647   {
648     case 11025:
649     case 22050:
650     case 44100:
651     case 88200:
652     case 176400:
653       audioFormat.mSampleRate = 44100;
654       break;
655     default:
656     case 8000:
657     case 12000:
658     case 16000:
659     case 24000:
660     case 32000:
661     case 48000:
662     case 96000:
663     case 192000:
664     case 384000:
665       audioFormat.mSampleRate = 48000;
666       break;
667   }
668   
669   if (forceRaw)//make sure input and output samplerate match for preventing resampling
670     audioFormat.mSampleRate = CAAudioUnitSink::getCoreAudioRealisedSampleRate();
671   
672   audioFormat.mFramesPerPacket = 1;
673   audioFormat.mChannelsPerFrame= 2;// ios only supports 2 channels
674   audioFormat.mBitsPerChannel  = CAEUtil::DataFormatToBits(format.m_dataFormat);
675   audioFormat.mBytesPerFrame   = format.m_frameSize;
676   audioFormat.mBytesPerPacket  = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
677   audioFormat.mFormatFlags    |= kLinearPCMFormatFlagIsPacked;
678   
679 #if DO_440HZ_TONE_TEST
680   SineWaveGeneratorInitWithFrequency(&m_SineWaveGenerator, 440.0, audioFormat.mSampleRate);
681 #endif
682
683   m_audioSink = new CAAudioUnitSink;
684   m_audioSink->open(audioFormat);
685
686   format.m_frames = m_audioSink->chunkSize();
687   format.m_frameSamples = format.m_frames * audioFormat.mChannelsPerFrame;
688   // reset to the realised samplerate
689   format.m_sampleRate = m_audioSink->getRealisedSampleRate();
690   m_format = format;
691
692   m_volume_changed = false;
693   m_audioSink->play(false);
694
695   return true;
696 }
697
698 void CAESinkDARWINIOS::Deinitialize()
699 {
700   delete m_audioSink;
701   m_audioSink = NULL;
702 }
703
704 bool CAESinkDARWINIOS::IsCompatible(const AEAudioFormat &format, const std::string &device)
705 {
706   return ((m_format.m_sampleRate    == format.m_sampleRate) &&
707           (m_format.m_dataFormat    == format.m_dataFormat) &&
708           (m_format.m_channelLayout == format.m_channelLayout));
709 }
710
711 double CAESinkDARWINIOS::GetDelay()
712 {
713   if (m_audioSink)
714     return m_audioSink->getDelay();
715   return 0.0;
716 }
717
718 double CAESinkDARWINIOS::GetCacheTotal()
719 {
720   if (m_audioSink)
721     return m_audioSink->cacheSize();
722   return 0.0;
723 }
724
725 unsigned int CAESinkDARWINIOS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
726 {
727   
728 #if DO_440HZ_TONE_TEST
729   if (m_format.m_dataFormat == AE_FMT_FLOAT)
730   {
731     float *samples = (float*)data;
732     for (unsigned int j = 0; j < frames ; j++)
733     {
734       float sample = SineWaveGeneratorNextSampleFloat(&m_SineWaveGenerator);
735       *samples++ = sample;
736       *samples++ = sample;
737     }
738     
739   }
740   else
741   {
742     int16_t *samples = (int16_t*)data;
743     for (unsigned int j = 0; j < frames ; j++)
744     {
745       int16_t sample = SineWaveGeneratorNextSampleInt16(&m_SineWaveGenerator);
746       *samples++ = sample;
747       *samples++ = sample;
748     }
749   }
750 #endif
751   if (m_audioSink)
752     return m_audioSink->write(data, frames);
753   return 0;
754 }
755
756 void CAESinkDARWINIOS::Drain()
757 {
758   if (m_audioSink)
759     m_audioSink->drain();
760 }
761
762 bool CAESinkDARWINIOS::HasVolume()
763 {
764   return false;
765 }
766
767 void  CAESinkDARWINIOS::SetVolume(float scale)
768 {
769   // CoreAudio uses fixed steps, reverse scale back to percent
770   float gain = CAEUtil::ScaleToGain(scale);
771   m_volume = CAEUtil::GainToPercent(gain);
772   m_volume_changed = true;
773 }
774
775 void CAESinkDARWINIOS::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
776 {
777   m_devices.clear();
778   EnumerateDevices(m_devices);
779   list = m_devices;
780 }