changed: Improve (fallback) logic for subtitle downloading
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / CoreAudioAE.cpp
1 /*
2  *      Copyright (C) 2011-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 "system.h"
22
23 #include "CoreAudioAE.h"
24
25 #include "CoreAudioAEStream.h"
26 #include "CoreAudioAESound.h"
27 #include "Application.h"
28 #include "cores/AudioEngine/Utils/AEUtil.h"
29 #include "settings/AdvancedSettings.h"
30 #include "settings/Settings.h"
31 #include "threads/SingleLock.h"
32 #include "utils/EndianSwap.h"
33 #include "utils/log.h"
34 #include "utils/TimeUtils.h"
35 #include "utils/MathUtils.h"
36 #include "threads/SystemClock.h"
37
38 #define DELAY_FRAME_TIME  20
39 #define BUFFERSIZE        16416
40
41 // on darwin when the devicelist changes
42 // reinit by calling opencoreaudio with the last engine parameters 
43 // (this will fallback to default
44 // device when our current output device vanishes
45 // and on the other hand will go back to that device
46 // if it re-appears).
47 #if defined(TARGET_DARWIN_OSX)
48 OSStatus deviceChangedCB( AudioObjectID                       inObjectID,
49                           UInt32                              inNumberAddresses,
50                           const AudioObjectPropertyAddress    inAddresses[],
51                           void*                               inClientData)
52 {
53   CCoreAudioAE *pEngine = (CCoreAudioAE *)inClientData;
54   if (pEngine->GetHAL())
55   {
56     pEngine->AudioDevicesChanged();
57     CLog::Log(LOGDEBUG, "CCoreAudioAE - audiodevicelist changed!");
58   }
59   return noErr;
60 }
61
62 void RegisterDeviceChangedCB(bool bRegister, void *ref)
63 {
64   OSStatus ret = noErr;
65   const AudioObjectPropertyAddress inAdr = 
66   {  
67     kAudioHardwarePropertyDevices,
68     kAudioObjectPropertyScopeGlobal,
69     kAudioObjectPropertyElementMaster 
70   };
71   
72   if (bRegister)
73     ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
74   else
75     ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
76
77   if (ret != noErr)
78     CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");   
79 }
80 #else//ios
81 void RegisterDeviceChangedCB(bool bRegister, void *ref){}
82 #endif
83
84 CCoreAudioAE::CCoreAudioAE() :
85   m_Initialized        (false         ),
86   m_deviceLost         (false         ),
87   m_callbackRunning    (false         ),
88   m_lastStreamFormat   (AE_FMT_INVALID),
89   m_lastChLayoutCount  (0             ),
90   m_lastSampleRate     (0             ),
91   m_chLayoutCount      (0             ),
92   m_rawPassthrough     (false         ),
93   m_volume             (1.0f          ),
94   m_volumeBeforeMute   (1.0f          ),
95   m_muted              (false         ),
96   m_soundMode          (AE_SOUND_OFF  ),
97   m_streamsPlaying     (false         ),
98   m_isSuspended        (false         ),
99   m_softSuspend        (false         ),
100   m_softSuspendTimer   (0             )
101 {
102   HAL = new CCoreAudioAEHAL;
103   
104   RegisterDeviceChangedCB(true, this);
105 }
106
107 CCoreAudioAE::~CCoreAudioAE()
108 {
109   RegisterDeviceChangedCB(false, this);
110   Shutdown();
111 }
112
113 void CCoreAudioAE::Shutdown()
114 {
115   CSingleLock engineLock(m_engineLock);
116
117   Stop();
118
119   Deinitialize();
120
121   /* free the streams */
122   CSingleLock streamLock(m_streamLock);
123   while (!m_streams.empty())
124   {
125     CCoreAudioAEStream *s = m_streams.front();
126     m_sounds.pop_front();
127     delete s;
128   }
129
130   /* free the sounds */
131   CSingleLock soundLock(m_soundLock);
132   while (!m_sounds.empty())
133   {
134     CCoreAudioAESound *s = m_sounds.front();
135     m_sounds.pop_front();
136     delete s;
137   }
138
139   delete HAL;
140   HAL = NULL;
141 }
142
143 void CCoreAudioAE::AudioDevicesChanged()
144 {
145   if (!m_Initialized && !m_deviceLost)
146     return;
147
148   CSingleLock engineLock(m_engineLock);
149
150   // re-check initialized since it can have changed when we waited and grabbed the lock
151   if (!m_Initialized && !m_deviceLost)
152     return;
153
154   OpenCoreAudio(m_lastSampleRate, COREAUDIO_IS_RAW(m_lastStreamFormat), m_lastStreamFormat, m_transcode);
155
156   // when we tried to open the default device or the last device
157   // again there was an error preventing us from doing it (mostly
158   // the device couldn't be found) - in that case
159   // mark our device as lost and hope that another callback
160   // for changed device list fires (e.x. device reappears)
161   if (!m_Initialized)
162       m_deviceLost = true;
163   else
164       m_deviceLost = false;
165 }
166
167 bool CCoreAudioAE::Initialize()
168 {
169   CSingleLock engineLock(m_engineLock);
170
171   Stop();
172
173   Deinitialize();
174
175   bool ret = OpenCoreAudio(44100, false, AE_FMT_FLOAT, false);
176   m_lastSampleRate = 44100;
177   m_lastStreamFormat = AE_FMT_FLOAT;
178
179   Start();
180
181   return ret;
182 }
183
184 bool CCoreAudioAE::OpenCoreAudio(unsigned int sampleRate, bool forceRaw,
185   enum AEDataFormat rawDataFormat, bool forceTranscode)
186 {
187   unsigned int maxChannelCountInStreams = 0;
188   // remove any deleted streams
189   CSingleLock streamLock(m_streamLock);
190   for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end();)
191   {
192     CCoreAudioAEStream *stream = *itt;
193     if (stream->IsDestroyed())
194     {
195       itt = m_streams.erase(itt);
196       delete stream;
197       continue;
198     }
199     else
200     {
201       // close all converter
202       stream->CloseConverter();
203     }
204
205     if (stream->GetChannelCount() > maxChannelCountInStreams)
206         maxChannelCountInStreams = stream->GetChannelCount();
207
208     ++itt;
209   }
210
211   /* override the sample rate based on the oldest stream if there is one */
212   if (!m_streams.empty())
213     sampleRate = m_streams.front()->GetSampleRate();
214
215   if (forceRaw)
216     m_rawPassthrough = true;
217   else
218     m_rawPassthrough = !m_streams.empty() && m_streams.front()->IsRaw();
219   streamLock.Leave();
220     
221   if (m_rawPassthrough)
222     CLog::Log(LOGINFO, "CCoreAudioAE::OpenCoreAudio - RAW passthrough enabled");
223
224   m_transcode = forceTranscode;
225   
226   if (m_transcode)
227     CLog::Log(LOGINFO, "CCoreAudioAE::OpenCoreAudio - transcode to ac3 enabled");
228   
229   std::string m_outputDevice =  CSettings::Get().GetString("audiooutput.audiodevice");
230
231   // on iOS devices we set fixed to two channels.
232   m_stdChLayout = AE_CH_LAYOUT_2_0;
233 #if defined(TARGET_DARWIN_OSX)
234   switch (CSettings::Get().GetInt("audiooutput.channels"))
235   {
236     default:
237     case  0: m_stdChLayout = AE_CH_LAYOUT_2_0; break; /* do not allow 1_0 output */
238     case  1: m_stdChLayout = AE_CH_LAYOUT_2_0; break;
239     case  2: m_stdChLayout = AE_CH_LAYOUT_2_1; break;
240     case  3: m_stdChLayout = AE_CH_LAYOUT_3_0; break;
241     case  4: m_stdChLayout = AE_CH_LAYOUT_3_1; break;
242     case  5: m_stdChLayout = AE_CH_LAYOUT_4_0; break;
243     case  6: m_stdChLayout = AE_CH_LAYOUT_4_1; break;
244     case  7: m_stdChLayout = AE_CH_LAYOUT_5_0; break;
245     case  8: m_stdChLayout = AE_CH_LAYOUT_5_1; break;
246     case  9: m_stdChLayout = AE_CH_LAYOUT_7_0; break;
247     case 10: m_stdChLayout = AE_CH_LAYOUT_7_1; break;
248   }
249 #endif
250
251   // setup the desired format
252   m_format.m_channelLayout = CAEChannelInfo(m_stdChLayout);
253
254   // if there is an audio resample rate set, use it.
255   if (CSettings::Get().GetInt("audiooutput.config") == AE_CONFIG_FIXED && !m_rawPassthrough)
256   {
257     sampleRate = CSettings::Get().GetInt("audiooutput.samplerate");
258     CLog::Log(LOGINFO, "CCoreAudioAE::passthrough - Forcing samplerate to %d", sampleRate);
259   }
260
261   if (m_rawPassthrough && !m_transcode)
262   {
263     switch (rawDataFormat)
264     {
265       case AE_FMT_AC3:
266       case AE_FMT_DTS:
267         m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_2_0);
268         m_format.m_sampleRate   = 48000;
269         m_format.m_dataFormat   = AE_FMT_S16NE;
270         break;
271       case AE_FMT_EAC3:
272         m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_2_0);
273         m_format.m_sampleRate   = 192000;
274         m_format.m_dataFormat   = AE_FMT_S16NE;
275         break;
276       case AE_FMT_DTSHD:
277       case AE_FMT_TRUEHD:
278         m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_7_1);
279         m_format.m_sampleRate   = 192000;
280         m_format.m_dataFormat   = AE_FMT_S16NE;
281         break;
282       case AE_FMT_LPCM:
283         // audio midi setup can be setup to 2.0 or 7.1
284         // if we have the number of max channels from streams we use that for
285         // selecting either 2.0 or 7.1 setup depending on that.
286         // This allows DPII modes on amps for enhancing stereo sound
287         // (when switching to 7.1 - all 8 channels will be pushed out preventing most amps
288         // to switch to DPII mode)
289         if (maxChannelCountInStreams == 1 || maxChannelCountInStreams == 2)
290           m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_2_0);
291         else
292           m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_7_1);
293         m_format.m_sampleRate   = sampleRate;
294         m_format.m_dataFormat   = AE_FMT_FLOAT;
295         break;
296       default:
297         break;
298     }
299   }
300   else if (m_transcode)
301   {
302     // transcode is to ac3 only, do we copy the ac3 settings from above
303     m_format.m_channelLayout    = CAEChannelInfo(AE_CH_LAYOUT_2_0);
304     m_format.m_sampleRate       = 48000;
305     m_format.m_dataFormat       = AE_FMT_S16NE;
306   }
307   else
308   {
309     m_format.m_sampleRate       = sampleRate;
310     m_format.m_channelLayout    = CAEChannelInfo(m_stdChLayout);
311     m_format.m_dataFormat       = AE_FMT_FLOAT;
312   }
313
314   m_format.m_encodedRate = 0;
315
316   if (m_outputDevice.empty())
317     m_outputDevice = "default";
318
319   AEAudioFormat initformat = m_format;
320
321   // initialize audio hardware
322   m_Initialized = HAL->Initialize(this, m_rawPassthrough || m_transcode, initformat, m_transcode ? AE_FMT_AC3 : rawDataFormat, m_outputDevice, m_volume);
323
324   unsigned int bps         = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
325   m_chLayoutCount          = m_format.m_channelLayout.Count();
326   m_format.m_frameSize     = (bps>>3) * m_chLayoutCount; //initformat.m_frameSize;
327   //m_format.m_frames        = (unsigned int)(((float)m_format.m_sampleRate / 1000.0f) * (float)DELAY_FRAME_TIME);
328   //m_format.m_frameSamples  = m_format.m_frames * m_format.m_channelLayout.Count();
329
330   if ((initformat.m_channelLayout.Count() != m_chLayoutCount) && !m_rawPassthrough && !m_transcode)
331   {
332     /* readjust parameters. hardware didn't accept channel count*/
333     CLog::Log(LOGINFO, "CCoreAudioAE::Initialize: Setup channels (%d) greater than possible hardware channels (%d).",
334               m_chLayoutCount, initformat.m_channelLayout.Count());
335
336     m_format.m_channelLayout = CAEChannelInfo(initformat.m_channelLayout);
337     m_chLayoutCount          = m_format.m_channelLayout.Count();
338     m_format.m_frameSize     = (bps>>3) * m_chLayoutCount; //initformat.m_frameSize;
339     //m_format.m_frameSamples  = m_format.m_frames * m_format.m_channelLayout.Count();
340   }
341
342   CLog::Log(LOGINFO, "CCoreAudioAE::Initialize:");
343   CLog::Log(LOGINFO, "  Output Device : %s", m_outputDevice.c_str());
344   CLog::Log(LOGINFO, "  Sample Rate   : %d", m_format.m_sampleRate);
345   CLog::Log(LOGINFO, "  Sample Format : %s", CAEUtil::DataFormatToStr(m_format.m_dataFormat));
346   CLog::Log(LOGINFO, "  Channel Count : %d", m_chLayoutCount);
347   CLog::Log(LOGINFO, "  Channel Layout: %s", ((std::string)m_format.m_channelLayout).c_str());
348   CLog::Log(LOGINFO, "  Frame Size    : %d", m_format.m_frameSize);
349   CLog::Log(LOGINFO, "  Volume Level  : %f", m_volume);
350   CLog::Log(LOGINFO, "  Passthrough   : %d", m_rawPassthrough);
351   CLog::Log(LOGINFO, "  Transcode     : %d", m_transcode);
352
353   CSingleLock soundLock(m_soundLock);
354   StopAllSounds();
355
356   // re-init sounds and unlock
357   for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
358     (*itt)->Initialize();
359
360   soundLock.Leave();
361
362   // if we are not in m_rawPassthrough reinit the streams
363   if (!m_rawPassthrough)
364   {
365     /* re-init streams */
366     streamLock.Enter();
367     for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
368       (*itt)->Initialize();
369     streamLock.Leave();
370   }
371
372   return m_Initialized;
373 }
374
375 void CCoreAudioAE::Deinitialize()
376 {
377   if (!m_Initialized)
378     return;
379
380   // close all open converters
381   CSingleLock streamLock(m_streamLock);
382   for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end();++itt)
383     (*itt)->CloseConverter();
384   streamLock.Leave();
385
386   m_Initialized = false;
387
388   CSingleLock callbackLock(m_callbackLock);
389
390   /*
391   while(m_callbackRunning)
392     Sleep(100);
393   */
394
395   HAL->Deinitialize();
396 }
397
398 void CCoreAudioAE::OnSettingsChange(const std::string& setting)
399 {
400   if (setting == "audiooutput.dontnormalizelevels")
401   {
402     // re-init streams remapper
403     CSingleLock streamLock(m_streamLock);
404     for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
405       (*itt)->InitializeRemap();
406     streamLock.Leave();
407   }
408
409   if (setting == "audiooutput.passthroughdevice" ||
410       setting == "audiooutput.custompassthrough" ||
411       setting == "audiooutput.audiodevice"       ||
412       setting == "audiooutput.customdevice"      ||
413       setting == "audiooutput.ac3passthrough"    ||
414       setting == "audiooutput.eac3passthrough"   ||
415       setting == "audiooutput.dtspassthrough"    ||
416       setting == "audiooutput.channels"          ||
417       setting == "audiooutput.samplerate"        ||
418       setting == "audiooutput.config"            ||
419       setting == "audiooutput.passthrough"        )
420   {
421     // only reinit the engine if we not
422     // suspended (resume will initialize
423     // us again in that case)
424     if (!m_isSuspended)
425       Initialize();
426   }
427 }
428
429 unsigned int CCoreAudioAE::GetSampleRate()
430 {
431   return m_format.m_sampleRate;
432 }
433
434 unsigned int CCoreAudioAE::GetEncodedSampleRate()
435 {
436   return m_format.m_encodedRate;
437 }
438
439 CAEChannelInfo CCoreAudioAE::GetChannelLayout()
440 {
441   return m_format.m_channelLayout;
442 }
443
444 unsigned int CCoreAudioAE::GetChannelCount()
445 {
446   return m_chLayoutCount;
447 }
448
449 enum AEDataFormat CCoreAudioAE::GetDataFormat()
450 {
451   return m_format.m_dataFormat;
452 }
453
454 AEAudioFormat CCoreAudioAE::GetAudioFormat()
455 {
456   return m_format;
457 }
458
459 double CCoreAudioAE::GetDelay()
460 {
461   return HAL->GetDelay();
462 }
463
464 float CCoreAudioAE::GetVolume()
465 {
466   return m_volume;
467 }
468
469 void CCoreAudioAE::SetVolume(float volume)
470 {
471   if (m_rawPassthrough)
472     return;
473
474   m_volume = volume;
475   // track volume if we are not muted
476   // we need this because m_volume is init'ed via
477   // SetVolume and need to also init m_volumeBeforeMute.
478   if (!m_muted)
479     m_volumeBeforeMute = volume;
480
481   HAL->SetVolume(m_volume);
482 }
483
484 void CCoreAudioAE::SetMute(const bool enabled)
485 {
486   m_muted = enabled;
487   if(m_muted)
488   {
489     m_volumeBeforeMute = m_volume;
490     SetVolume(VOLUME_MINIMUM);
491   }
492   else
493   {
494     SetVolume(m_volumeBeforeMute);
495   }
496 }
497
498 bool CCoreAudioAE::IsMuted()
499 {
500   return m_muted;
501 }
502
503 bool CCoreAudioAE::IsSuspended()
504 {
505   return m_isSuspended;
506 }
507
508 void CCoreAudioAE::SetSoundMode(const int mode)
509 {
510   m_soundMode = mode;
511
512   /* stop all currently playing sounds if they are being turned off */
513   if (mode == AE_SOUND_OFF || (mode == AE_SOUND_IDLE && m_streamsPlaying))
514     StopAllSounds();
515 }
516
517 bool CCoreAudioAE::SupportsRaw(AEDataFormat format)
518 {
519   switch(format)
520   {
521     case AE_FMT_AC3:
522     case AE_FMT_DTS:
523     case AE_FMT_EAC3:
524     case AE_FMT_LPCM:
525       return true;
526     default:
527       return false;
528   }
529 }
530
531 bool CCoreAudioAE::IsSettingVisible(const std::string &settingId)
532 {
533   if (settingId == "audiooutput.samplerate")
534   {
535     if (CSettings::Get().GetInt("audiooutput.config") == AE_CONFIG_FIXED)
536       return true;
537     else
538       return false;
539   }
540   else if (settingId == "audiooutput.stereoupmix")
541   {
542     if (CSettings::Get().GetInt("audiooutput.channels") > AE_CH_LAYOUT_2_0)
543       return true;
544   }
545
546   return true;
547 }
548
549 CCoreAudioAEHAL* CCoreAudioAE::GetHAL()
550 {
551   return HAL;
552 }
553
554 IAEStream* CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
555   unsigned int sampleRate, unsigned int encodedSamplerate, CAEChannelInfo channelLayout, unsigned int options)
556 {
557   // if we are suspended we don't
558   // want anyone to mess with us
559   if (m_isSuspended && !m_softSuspend)
560 #if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV)
561     Resume();
562 #else
563     return NULL;
564 #endif
565
566   CAEChannelInfo channelInfo(channelLayout);
567   CLog::Log(LOGINFO, "CCoreAudioAE::MakeStream - %s, %u, %u, %s",
568     CAEUtil::DataFormatToStr(dataFormat), sampleRate, encodedSamplerate, ((std::string)channelInfo).c_str());
569
570   bool multichannelpcm = CSettings::Get().GetInt("audiooutput.channels") > AE_CH_LAYOUT_2_0; //if more then 2 channels are set - assume lpcm capability
571 #if defined(TARGET_DARWIN_IOS)
572   multichannelpcm = false;
573 #endif
574   // determine if we need to transcode this audio
575   // when we're called, we'll either get the audio in an encoded form (COREAUDIO_IS_RAW==true)
576   // that we can passthrough based on user options, or we'll get it unencoded
577   // if it's unencoded, and is 5.1, we'll transcode it to AC3 if possible
578   bool transcode = CSettings::Get().GetBool("audiooutput.passthrough") && CSettings::Get().GetBool("audiooutput.ac3passthrough") && !multichannelpcm &&
579                    !COREAUDIO_IS_RAW(dataFormat) &&
580                   (channelInfo.Count() == 6);
581   
582   CCoreAudioAEStream *stream = new CCoreAudioAEStream(dataFormat, sampleRate, encodedSamplerate, channelLayout, options, transcode);
583   CSingleLock streamLock(m_streamLock);
584   m_streams.push_back(stream);
585   streamLock.Leave();
586
587   if ((options & AESTREAM_PAUSED) == 0)
588     Stop();
589
590   // reinit the engine if pcm format changes or always on raw format or always on transcode
591   if (m_Initialized && ( m_lastStreamFormat != dataFormat ||
592                          m_lastChLayoutCount != channelLayout.Count() ||
593                          m_lastSampleRate != sampleRate ||
594                          COREAUDIO_IS_RAW(dataFormat) ||
595                          transcode))
596   {
597     CSingleLock engineLock(m_engineLock);
598     Stop();
599     Deinitialize();
600     m_Initialized = OpenCoreAudio(sampleRate, COREAUDIO_IS_RAW(dataFormat), dataFormat, transcode);
601     m_lastStreamFormat = dataFormat;
602     m_lastChLayoutCount = channelLayout.Count();
603     m_lastSampleRate = sampleRate;
604   }
605
606   /* if the stream was not initialized, do it now */
607   if (!stream->IsValid())
608     stream->Initialize();
609
610   if ((options & AESTREAM_PAUSED) == 0)  
611     Start();
612
613   m_streamsPlaying = true;
614
615   return stream;
616 }
617
618 IAEStream* CCoreAudioAE::FreeStream(IAEStream *stream)
619 {
620   CSingleLock streamLock(m_streamLock);
621   /* ensure the stream still exists */
622   for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); )
623   {
624     CCoreAudioAEStream *del = *itt;
625     if (*itt == stream)
626     {
627       itt = m_streams.erase(itt);
628       delete (CCoreAudioAEStream *)stream;
629     }
630     else if (del->IsDestroyed())
631     {
632       itt = m_streams.erase(itt);
633       delete del;
634     }
635     else
636     {
637       ++itt;
638     }
639
640   }
641   m_streamsPlaying = !m_streams.empty();
642
643   streamLock.Leave();
644
645   // When we have been in passthrough mode and are not suspended,
646   // reinit the hardware to come back to anlog out
647   if (/*m_streams.empty() || */ m_rawPassthrough && !m_isSuspended)
648   {
649     CLog::Log(LOGINFO, "CCoreAudioAE::FreeStream Reinit, no streams left" );
650     Initialize();
651   }
652
653   return NULL;
654 }
655
656 void CCoreAudioAE::PlaySound(IAESound *sound)
657 {
658   if (m_soundMode == AE_SOUND_OFF || (m_soundMode == AE_SOUND_IDLE && m_streamsPlaying) || (m_isSuspended && !m_softSuspend))
659     return;
660
661   float *samples = ((CCoreAudioAESound*)sound)->GetSamples();
662   if (!samples && !m_Initialized)
663     return;
664
665   /* add the sound to the play list */
666   CSingleLock soundSampleLock(m_soundSampleLock);
667   SoundState ss = {
668     ((CCoreAudioAESound*)sound),
669     samples,
670     ((CCoreAudioAESound*)sound)->GetSampleCount()
671   };
672   m_playing_sounds.push_back(ss);
673 }
674
675 void CCoreAudioAE::StopSound(IAESound *sound)
676 {
677   CSingleLock lock(m_soundSampleLock);
678   for (SoundStateList::iterator itt = m_playing_sounds.begin(); itt != m_playing_sounds.end(); )
679   {
680     if ((*itt).owner == sound)
681     {
682       (*itt).owner->ReleaseSamples();
683       itt = m_playing_sounds.erase(itt);
684     }
685     else
686       ++itt;
687   }
688 }
689
690 IAESound *CCoreAudioAE::MakeSound(const std::string& file)
691 {
692   // we don't make sounds
693   // when suspended
694   if (m_isSuspended)
695     return NULL;
696
697   CSingleLock soundLock(m_soundLock);
698
699   // first check if we have the file cached
700   for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
701   {
702     if ((*itt)->GetFileName() == file)
703       return *itt;
704   }
705
706   CCoreAudioAESound *sound = new CCoreAudioAESound(file);
707   if (!sound->Initialize())
708   {
709     delete sound;
710     return NULL;
711   }
712
713   m_sounds.push_back(sound);
714   return sound;
715 }
716
717 void CCoreAudioAE::FreeSound(IAESound *sound)
718 {
719   if (!sound)
720     return;
721
722   sound->Stop();
723   CSingleLock soundLock(m_soundLock);
724   for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
725     if (*itt == sound)
726     {
727       m_sounds.erase(itt);
728       break;
729     }
730
731   delete (CCoreAudioAESound*)sound;
732 }
733
734 void CCoreAudioAE::StopAllSounds()
735 {
736   CSingleLock lock(m_soundSampleLock);
737   while (!m_playing_sounds.empty())
738   {
739     SoundState *ss = &(*m_playing_sounds.begin());
740     m_playing_sounds.pop_front();
741     ss->owner->ReleaseSamples();
742   }
743 }
744
745 void CCoreAudioAE::MixSounds(float *buffer, unsigned int samples)
746 {
747   if (!m_Initialized)
748     return;
749
750   SoundStateList::iterator itt;
751
752   CSingleLock lock(m_soundSampleLock);
753   for (itt = m_playing_sounds.begin(); itt != m_playing_sounds.end(); )
754   {
755     SoundState *ss = &(*itt);
756
757     // no more frames, so remove it from the list
758     if (ss->sampleCount == 0)
759     {
760       ss->owner->ReleaseSamples();
761       itt = m_playing_sounds.erase(itt);
762       continue;
763     }
764
765     unsigned int mixSamples = std::min(ss->sampleCount, samples);
766     float volume = ss->owner->GetVolume();
767
768     for (unsigned int i = 0; i < mixSamples; ++i)
769       buffer[i] = (buffer[i] + (ss->samples[i] * volume));
770
771     ss->sampleCount -= mixSamples;
772     ss->samples     += mixSamples;
773
774     ++itt;
775   }
776 }
777
778 void CCoreAudioAE::GarbageCollect()
779 {
780 #if defined(TARGET_DARWIN_OSX)
781   if (CSettings::Get().GetInt("audiooutput.streamsilence") != 0)
782     return;
783   
784   if (!m_streamsPlaying && m_playing_sounds.empty())
785   {
786     if (!m_softSuspend)
787     {
788       m_softSuspend = true;
789       m_softSuspendTimer = XbmcThreads::SystemClockMillis() + 10000; //10.0 second delay for softSuspend
790     }
791   }
792   else
793   {
794     if (m_isSuspended)
795     {
796       CSingleLock engineLock(m_engineLock);
797       CLog::Log(LOGDEBUG, "CCoreAudioAE::GarbageCollect - Acquire CA HAL.");
798       Start();
799       m_isSuspended = false;
800     }
801     m_softSuspend = false;
802   }
803   
804   unsigned int curSystemClock = XbmcThreads::SystemClockMillis();
805   if (!m_isSuspended && m_softSuspend && curSystemClock > m_softSuspendTimer)
806   {
807     Suspend();// locks m_engineLock internally
808     CLog::Log(LOGDEBUG, "CCoreAudioAE::GarbageCollect - Release CA HAL.");
809   }
810 #endif // TARGET_DARWIN_OSX
811 }
812
813 void CCoreAudioAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
814 {
815   if (m_isSuspended && !m_softSuspend)
816     return;
817
818   HAL->EnumerateOutputDevices(devices, passthrough);
819 }
820
821 void CCoreAudioAE::Start()
822 {
823   if (!m_Initialized)
824     return;
825
826   HAL->Start();
827 }
828
829 void CCoreAudioAE::Stop()
830 {
831   if (!m_Initialized)
832     return;
833
834   HAL->Stop();
835 }
836
837 bool CCoreAudioAE::Suspend()
838 {
839   CSingleLock engineLock(m_engineLock);
840   CLog::Log(LOGDEBUG, "CCoreAudioAE::Suspend - Suspending AE processing");
841   m_isSuspended = true;
842   // stop all gui sounds
843   StopAllSounds();
844   // stop the CA thread
845   Stop();
846
847   return true;
848 }
849
850 bool CCoreAudioAE::Resume()
851 {
852   // fire up the engine again
853   bool ret = Initialize();
854   CLog::Log(LOGDEBUG, "CCoreAudioAE::Resume - Resuming AE processing");
855   m_isSuspended = false;
856
857   return ret;
858 }
859
860 //***********************************************************************************************
861 // Rendering Methods
862 //***********************************************************************************************
863 OSStatus CCoreAudioAE::OnRender(AudioUnitRenderActionFlags *actionFlags,
864   const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
865 {
866   UInt32 frames = inNumberFrames;
867
868   unsigned int rSamples = frames * m_chLayoutCount;
869   int size = frames * m_format.m_frameSize;
870   //int size = frames * HAL->m_BytesPerFrame;
871
872   for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
873     bzero(ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize);
874
875   if (!m_Initialized)
876   {
877     m_callbackRunning = false;
878     return noErr;
879   }
880
881   CSingleLock callbackLock(m_callbackLock);
882
883   m_callbackRunning = true;
884
885   /*
886   CSingleLock streamLock(m_streamLock);
887   // Remove any destroyed stream
888   if (!m_streams.empty())
889   {
890     for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end();)
891     {
892       CCoreAudioAEStream *stream = *itt;
893
894       if (stream->IsDestroyed())
895       {
896         itt = m_streams.erase(itt);
897         delete stream;
898         continue;
899       }
900       ++itt;
901     }
902   }
903   streamLock.Leave();
904   */
905
906   // when not in passthrough output mix sounds
907   if (!m_rawPassthrough && m_soundMode != AE_SOUND_OFF)
908   {
909     MixSounds((float *)ioData->mBuffers[0].mData, rSamples);
910     ioData->mBuffers[0].mDataByteSize = size;
911     if (!size && actionFlags)
912       *actionFlags |=  kAudioUnitRenderAction_OutputIsSilence;
913   }
914
915   m_callbackRunning = false;
916
917   return noErr;
918 }
919
920 // Static Callback from AudioUnit
921 OSStatus CCoreAudioAE::Render(AudioUnitRenderActionFlags* actionFlags,
922   const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
923 {
924   OSStatus ret = OnRender(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
925
926   return ret;
927 }
928
929