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