#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)
{
if (delay < 0)
delay = 0.0;
- delay += stream->m_bufferedTime;
+ delay += stream->m_bufferedTime / stream->m_streamResampleRatio;
return delay;
}
float CEngineStats::GetWaterLevel()
{
+ CSingleLock lock(m_lock);
return (float)m_bufferedSamples / m_sinkSampleRate;
}
m_vizBuffers = NULL;
m_vizBuffersInput = NULL;
m_volume = 1.0;
+ m_volumeScaled = 1.0;
m_aeVolume = 1.0;
m_muted = false;
m_aeMuted = false;
void CActiveAE::Dispose()
{
-#if defined(HAS_GLX) || defined(TARGET_DARWIN_OSX)
+#if defined(HAS_GLX) || defined(TARGET_DARWIN)
g_Windowing.Unregister(this);
#endif
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;
return;
case CActiveAEControlProtocol::DISPLAYRESET:
return;
+ case CActiveAEControlProtocol::APPFOCUSED:
+ m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::APPFOCUSED, msg->data, sizeof(bool));
+ return;
default:
break;
}
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();
}
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:
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();
}
m_stats.SetSuspended(false);
m_extDeferData = false;
return;
+ case CActiveAEControlProtocol::DEVICECHANGE:
+ return;
default:
break;
}
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;
}
}
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;
// 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)
outputFormat = inputFormat;
outputFormat.m_dataFormat = AE_FMT_FLOATP;
outputFormat.m_sampleRate = 48000;
+ outputFormat.m_encodedRate = 48000;
// setup encoder
if (!m_encoder)
// 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;
}
}
}
+ }
- // 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;
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
}
}
}
// 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
}
}
-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;
}
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;
}
m_controlPort.SendOutMessage(CActiveAEControlProtocol::KEEPCONFIG, &timeMs, sizeof(unsigned int));
}
+void CActiveAE::DeviceChange()
+{
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::DEVICECHANGE);
+}
+
void CActiveAE::OnLostDevice()
{
Message *reply;
m_controlPort.SendOutMessage(CActiveAEControlProtocol::DISPLAYRESET);
}
+void CActiveAE::OnAppFocusChange(bool focus)
+{
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::APPFOCUSED, &focus, sizeof(focus));
+}
+
//-----------------------------------------------------------------------------
// Utils
//-----------------------------------------------------------------------------
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;