Merge pull request #4878 from FernetMenta/xfade
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / ActiveAE / ActiveAE.cpp
index f30f37b..eae6253 100644 (file)
@@ -32,6 +32,7 @@ using namespace ActiveAE;
 
 #define MAX_CACHE_LEVEL 0.5   // total cache time of stream in seconds
 #define MAX_WATER_LEVEL 0.25  // buffered time after stream stages in seconds
+#define MAX_BUFFER_TIME 0.1   // max time of a buffer in seconds
 
 void CEngineStats::Reset(unsigned int sampleRate)
 {
@@ -101,7 +102,7 @@ float CEngineStats::GetDelay(CActiveAEStream *stream)
   if (delay < 0)
     delay = 0.0;
 
-  delay += stream->m_bufferedTime;
+  delay += stream->m_bufferedTime / stream->m_streamResampleRatio;
   return delay;
 }
 
@@ -121,6 +122,7 @@ float CEngineStats::GetCacheTotal(CActiveAEStream *stream)
 
 float CEngineStats::GetWaterLevel()
 {
+  CSingleLock lock(m_lock);
   return (float)m_bufferedSamples / m_sinkSampleRate;
 }
 
@@ -148,6 +150,7 @@ CActiveAE::CActiveAE() :
   m_vizBuffers = NULL;
   m_vizBuffersInput = NULL;
   m_volume = 1.0;
+  m_volumeScaled = 1.0;
   m_aeVolume = 1.0;
   m_muted = false;
   m_aeMuted = false;
@@ -165,7 +168,7 @@ CActiveAE::~CActiveAE()
 
 void CActiveAE::Dispose()
 {
-#if defined(HAS_GLX) || defined(TARGET_DARWIN_OSX)
+#if defined(HAS_GLX) || defined(TARGET_DARWIN)
   g_Windowing.Unregister(this);
 #endif
 
@@ -224,6 +227,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
           return;
         case CActiveAEControlProtocol::VOLUME:
           m_volume = *(float*)msg->data;
+          m_volumeScaled = CAEUtil::GainToScale(CAEUtil::PercentToGain(m_volume));
           if (m_sinkHasVolume)
             m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::VOLUME, &m_volume, sizeof(float));
           return;
@@ -235,6 +239,9 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
           return;
         case CActiveAEControlProtocol::DISPLAYRESET:
           return;
+        case CActiveAEControlProtocol::APPFOCUSED:
+          m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::APPFOCUSED, msg->data, sizeof(bool));
+          return;
         default:
           break;
         }
@@ -392,13 +399,14 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
     case AE_TOP_CONFIGURED:
       if (port == &m_controlPort)
       {
+        bool streaming;
         switch (signal)
         {
         case CActiveAEControlProtocol::RECONFIGURE:
           if (m_streams.empty())
           {
-            bool silence = false;
-            m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::SILENCEMODE, &silence, sizeof(bool));
+            streaming = false;
+            m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::STREAMING, &streaming, sizeof(bool));
           }
           LoadSettings();
           ChangeResamplers();
@@ -425,16 +433,53 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
           }
           msg->Reply(CActiveAEControlProtocol::ACC);
           return;
+        case CActiveAEControlProtocol::DEVICECHANGE:
+          time_t now;
+          time(&now);
+          CLog::Log(LOGDEBUG,"CActiveAE - device change event");
+          while (!m_extLastDeviceChange.empty() && (now - m_extLastDeviceChange.front() > 0))
+          {
+            m_extLastDeviceChange.pop();
+          }
+          if (m_extLastDeviceChange.size() > 2)
+          {
+            CLog::Log(LOGWARNING,"CActiveAE - received %ld device change events within one second", m_extLastDeviceChange.size());
+            return;
+          }
+          m_extLastDeviceChange.push(now);
+          UnconfigureSink();
+          m_controlPort.PurgeOut(CActiveAEControlProtocol::DEVICECHANGE);
+          m_sink.EnumerateSinkList(true);
+          LoadSettings();
+          m_extError = false;
+          Configure();
+          if (!m_extError)
+          {
+            m_state = AE_TOP_CONFIGURED_PLAY;
+            m_extTimeout = 0;
+          }
+          else
+          {
+            m_state = AE_TOP_ERROR;
+            m_extTimeout = 500;
+          }
+          return;
         case CActiveAEControlProtocol::PAUSESTREAM:
           CActiveAEStream *stream;
           stream = *(CActiveAEStream**)msg->data;
           if (stream->m_paused != true && m_streams.size() == 1)
+          {
             FlushEngine();
+            streaming = false;
+            m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::STREAMING, &streaming, sizeof(bool));
+          }
           stream->m_paused = true;
           return;
         case CActiveAEControlProtocol::RESUMESTREAM:
           stream = *(CActiveAEStream**)msg->data;
           stream->m_paused = false;
+          streaming = true;
+          m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::STREAMING, &streaming, sizeof(bool));
           m_extTimeout = 0;
           return;
         case CActiveAEControlProtocol::FLUSHSTREAM:
@@ -591,11 +636,13 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
         switch (signal)
         {
         case CActiveAEControlProtocol::DISPLAYRESET:
+          CLog::Log(LOGDEBUG,"CActiveAE - display reset event");
           displayReset = true;
         case CActiveAEControlProtocol::INIT:
           m_extError = false;
           if (!displayReset)
           {
+            m_controlPort.PurgeOut(CActiveAEControlProtocol::DEVICECHANGE);
             m_sink.EnumerateSinkList(true);
             LoadSettings();
           }
@@ -615,6 +662,8 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
           m_stats.SetSuspended(false);
           m_extDeferData = false;
           return;
+        case CActiveAEControlProtocol::DEVICECHANGE:
+          return;
         default:
           break;
         }
@@ -912,11 +961,11 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt)
     m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::VOLUME, &m_volume, sizeof(float));
 
     // limit buffer size in case of sink returns large buffer
-    unsigned int buffertime = (m_sinkFormat.m_frames*1000) / m_sinkFormat.m_sampleRate;
-    if (buffertime > 80)
+    unsigned int buffertime = m_sinkFormat.m_frames / m_sinkFormat.m_sampleRate;
+    if (buffertime > MAX_BUFFER_TIME)
     {
-      CLog::Log(LOGWARNING, "ActiveAE::%s - sink returned large buffer of %d ms, reducing to 80 ms", __FUNCTION__, buffertime);
-      m_sinkFormat.m_frames = 80 * m_sinkFormat.m_sampleRate / 1000;
+      CLog::Log(LOGWARNING, "ActiveAE::%s - sink returned large buffer of %d ms, reducing to %d ms", __FUNCTION__, buffertime, (int)(MAX_BUFFER_TIME*1000));
+      m_sinkFormat.m_frames = MAX_BUFFER_TIME * m_sinkFormat.m_sampleRate;
     }
   }
 
@@ -943,8 +992,8 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt)
     sinkInputFormat = inputFormat;
     m_internalFormat = inputFormat;
 
-    bool silence = false;
-    m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::SILENCEMODE, &silence, sizeof(bool));
+    bool streaming = false;
+    m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::STREAMING, &streaming, sizeof(bool));
 
     delete m_encoder;
     m_encoder = NULL;
@@ -968,8 +1017,8 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt)
   // resample buffers for streams
   else
   {
-    bool silence = true;
-    m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::SILENCEMODE, &silence, sizeof(bool));
+    bool streaming = true;
+    m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::STREAMING, &streaming, sizeof(bool));
 
     AEAudioFormat outputFormat;
     if (m_mode == MODE_RAW)
@@ -984,6 +1033,7 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt)
       outputFormat = inputFormat;
       outputFormat.m_dataFormat = AE_FMT_FLOATP;
       outputFormat.m_sampleRate = 48000;
+      outputFormat.m_encodedRate = 48000;
 
       // setup encoder
       if (!m_encoder)
@@ -1120,7 +1170,9 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt)
 
   // resample buffers for sink
   if (m_sinkBuffers && 
-     (!CompareFormat(m_sinkBuffers->m_format,m_sinkFormat) || !CompareFormat(m_sinkBuffers->m_inputFormat, sinkInputFormat)))
+     (!CompareFormat(m_sinkBuffers->m_format,m_sinkFormat) ||
+      !CompareFormat(m_sinkBuffers->m_inputFormat, sinkInputFormat) ||
+      m_sinkBuffers->m_format.m_frames != m_sinkFormat.m_frames))
   {
     m_discardBufferPools.push_back(m_sinkBuffers);
     m_sinkBuffers = NULL;
@@ -1369,6 +1421,7 @@ void CActiveAE::ApplySettingsToFormat(AEAudioFormat &format, AudioSettings &sett
   {
     format.m_dataFormat = AE_FMT_AC3;
     format.m_sampleRate = 48000;
+    format.m_encodedRate = 48000;
     format.m_channelLayout = AE_CH_LAYOUT_2_0;
     if (mode)
       *mode = MODE_TRANSCODE;
@@ -1593,13 +1646,15 @@ bool CActiveAE::RunStages()
     CSampleBuffer *buffer;
     if (!(*it)->m_drain)
     {
-      while (time < MAX_CACHE_LEVEL && !(*it)->m_inputBuffers->m_freeSamples.empty())
+      float buftime = (float)(*it)->m_inputBuffers->m_format.m_frames / (*it)->m_inputBuffers->m_format.m_sampleRate;
+      time += buftime * (*it)->m_processingSamples.size();
+      while ((time < MAX_CACHE_LEVEL || (*it)->m_streamIsBuffering) && !(*it)->m_inputBuffers->m_freeSamples.empty())
       {
         buffer = (*it)->m_inputBuffers->GetFreeBuffer();
         (*it)->m_processingSamples.push_back(buffer);
         (*it)->m_streamPort->SendInMessage(CActiveAEDataProtocol::STREAMBUFFER, &buffer, sizeof(CSampleBuffer*));
         (*it)->IncFreeBuffers();
-        time += (float)buffer->pkt->max_nb_samples / buffer->pkt->config.sample_rate;
+        time += buftime;
       }
     }
     else
@@ -1692,7 +1747,14 @@ bool CActiveAE::RunStages()
             if ((*it)->m_fadingSamples == -1)
             {
               (*it)->m_fadingSamples = m_internalFormat.m_sampleRate * (float)(*it)->m_fadingTime / 1000.0f;
-              (*it)->m_volume = (*it)->m_fadingBase;
+              if ((*it)->m_fadingSamples > 0)
+                (*it)->m_volume = (*it)->m_fadingBase;
+              else
+              {
+                (*it)->m_volume = (*it)->m_fadingTarget;
+                CSingleLock lock((*it)->m_streamLock);
+                (*it)->m_streamFading = false;
+              }
             }
             if ((*it)->m_fadingSamples > 0)
             {
@@ -1922,7 +1984,7 @@ bool CActiveAE::RunStages()
       CSampleBuffer *buffer;
       for (it = m_streams.begin(); it != m_streams.end(); ++it)
       {
-        if (!(*it)->m_resampleBuffers->m_outputSamples.empty())
+        if (!(*it)->m_resampleBuffers->m_outputSamples.empty() && !(*it)->m_paused)
         {
           buffer =  (*it)->m_resampleBuffers->m_outputSamples.front();
           (*it)->m_resampleBuffers->m_outputSamples.pop_front();
@@ -1931,18 +1993,18 @@ bool CActiveAE::RunStages()
         }
       }
     }
+  }
 
-    // serve sink buffers
-    busy = m_sinkBuffers->ResampleBuffers();
-    while(!m_sinkBuffers->m_outputSamples.empty())
-    {
-      CSampleBuffer *out = NULL;
-      out = m_sinkBuffers->m_outputSamples.front();
-      m_sinkBuffers->m_outputSamples.pop_front();
-      m_sink.m_dataPort.SendOutMessage(CSinkDataProtocol::SAMPLE,
-          &out, sizeof(CSampleBuffer*));
-      busy = true;
-    }
+  // serve sink buffers
+  busy |= m_sinkBuffers->ResampleBuffers();
+  while(!m_sinkBuffers->m_outputSamples.empty())
+  {
+    CSampleBuffer *out = NULL;
+    out = m_sinkBuffers->m_outputSamples.front();
+    m_sinkBuffers->m_outputSamples.pop_front();
+    m_sink.m_dataPort.SendOutMessage(CSinkDataProtocol::SAMPLE,
+        &out, sizeof(CSampleBuffer*));
+    busy = true;
   }
 
   return busy;
@@ -2021,20 +2083,21 @@ void CActiveAE::MixSounds(CSoundPacket &dstSample)
 
 void CActiveAE::Deamplify(CSoundPacket &dstSample)
 {
-  if (m_volume < 1.0 || m_muted)
+  if (m_volumeScaled < 1.0 || m_muted)
   {
     float *buffer;
     int nb_floats = dstSample.nb_samples * dstSample.config.channels / dstSample.planes;
+    float volume = m_muted ? 0.0f : m_volumeScaled;
 
     for(int j=0; j<dstSample.planes; j++)
     {
       buffer = (float*)dstSample.data[j];
 #ifdef __SSE__
-      CAEUtil::SSEMulArray(buffer, m_muted ? 0.0 : m_volume, nb_floats);
+      CAEUtil::SSEMulArray(buffer, volume, nb_floats);
 #else
       float *fbuffer = buffer;
       for (int i = 0; i < nb_floats; i++)
-        *fbuffer++ *= m_volume;
+        *fbuffer++ *= volume;
 #endif
     }
   }
@@ -2083,7 +2146,7 @@ bool CActiveAE::Initialize()
   Message *reply;
   if (m_controlPort.SendOutMessageSync(CActiveAEControlProtocol::INIT,
                                                  &reply,
-                                                 5000))
+                                                 10000))
   {
     bool success = reply->signal == CActiveAEControlProtocol::ACC;
     reply->Release();
@@ -2102,7 +2165,7 @@ bool CActiveAE::Initialize()
   }
 
   // hook into windowing for receiving display reset events
-#if defined(HAS_GLX) || defined(TARGET_DARWIN_OSX)
+#if defined(HAS_GLX) || defined(TARGET_DARWIN
   g_Windowing.Register(this);
 #endif
 
@@ -2144,9 +2207,9 @@ void CActiveAE::OnSettingsChange(const std::string& setting)
   }
 }
 
-bool CActiveAE::SupportsRaw(AEDataFormat format)
+bool CActiveAE::SupportsRaw(AEDataFormat format, int samplerate)
 {
-  if (!m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), format))
+  if (!m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), format, samplerate))
     return false;
 
   return true;
@@ -2186,19 +2249,19 @@ bool CActiveAE::IsSettingVisible(const std::string &settingId)
   }
   else if (settingId == "audiooutput.truehdpassthrough")
   {
-    if (m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), AE_FMT_TRUEHD) &&
+    if (m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), AE_FMT_TRUEHD, 192000) &&
         CSettings::Get().GetInt("audiooutput.config") != AE_CONFIG_FIXED)
       return true;
   }
   else if (settingId == "audiooutput.dtshdpassthrough")
   {
-    if (m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), AE_FMT_DTSHD) &&
+    if (m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), AE_FMT_DTSHD, 192000) &&
         CSettings::Get().GetInt("audiooutput.config") != AE_CONFIG_FIXED)
       return true;
   }
   else if (settingId == "audiooutput.eac3passthrough")
   {
-    if (m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), AE_FMT_EAC3) &&
+    if (m_sink.SupportsFormat(CSettings::Get().GetString("audiooutput.passthroughdevice"), AE_FMT_EAC3, 192000) &&
         CSettings::Get().GetInt("audiooutput.config") != AE_CONFIG_FIXED)
       return true;
   }
@@ -2292,6 +2355,11 @@ void CActiveAE::KeepConfiguration(unsigned int millis)
   m_controlPort.SendOutMessage(CActiveAEControlProtocol::KEEPCONFIG, &timeMs, sizeof(unsigned int));
 }
 
+void CActiveAE::DeviceChange()
+{
+  m_controlPort.SendOutMessage(CActiveAEControlProtocol::DEVICECHANGE);
+}
+
 void CActiveAE::OnLostDevice()
 {
   Message *reply;
@@ -2317,6 +2385,11 @@ void CActiveAE::OnResetDevice()
   m_controlPort.SendOutMessage(CActiveAEControlProtocol::DISPLAYRESET);
 }
 
+void CActiveAE::OnAppFocusChange(bool focus)
+{
+  m_controlPort.SendOutMessage(CActiveAEControlProtocol::APPFOCUSED, &focus, sizeof(focus));
+}
+
 //-----------------------------------------------------------------------------
 // Utils
 //-----------------------------------------------------------------------------
@@ -2583,6 +2656,9 @@ bool CActiveAE::ResampleSound(CActiveAESound *sound)
 
 IAEStream *CActiveAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options)
 {
+  if (IsSuspended())
+    return NULL;
+
   //TODO: pass number of samples in audio packet
 
   AEAudioFormat format;