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