aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--language/English/strings.po16
-rw-r--r--lib/DllAvUtil.h22
-rw-r--r--lib/DllSwResample.h12
-rw-r--r--project/VS2010Express/XBMC.vcxproj16
-rw-r--r--project/VS2010Express/XBMC.vcxproj.filters47
-rw-r--r--system/settings/settings.xml14
-rw-r--r--system/settings/win32.xml28
-rw-r--r--xbmc/addons/Visualisation.cpp2
-rw-r--r--xbmc/cores/AudioEngine/AEAudioFormat.h7
-rw-r--r--xbmc/cores/AudioEngine/AEFactory.cpp67
-rw-r--r--xbmc/cores/AudioEngine/AEFactory.h14
-rw-r--r--xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp66
-rw-r--r--xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h3
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp2311
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h338
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp360
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.h112
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp266
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h62
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp863
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.h138
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.cpp161
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.h73
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp362
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h117
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.cpp2
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.h2
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.cpp2
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.h2
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp2
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h2
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AE.h32
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AEEncoder.h13
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AESink.h2
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AEStream.h2
-rw-r--r--xbmc/cores/AudioEngine/Makefile.in7
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp27
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkALSA.h3
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp7
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h2
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp91
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h3
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp2
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkNULL.h2
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp4
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkOSS.h2
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp2
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h2
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp179
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h7
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEUtil.cpp14
-rw-r--r--xbmc/cores/dvdplayer/DVDAudio.cpp2
-rw-r--r--xbmc/cores/paplayer/PAPlayer.cpp2
-rw-r--r--xbmc/settings/Settings.cpp22
-rw-r--r--xbmc/utils/ActorProtocol.cpp253
-rw-r--r--xbmc/utils/ActorProtocol.h87
-rw-r--r--xbmc/utils/Makefile.in1
57 files changed, 6151 insertions, 108 deletions
diff --git a/language/English/strings.po b/language/English/strings.po
index 55d187099c..6fbd2697dc 100644
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -1762,7 +1762,10 @@ msgctxt "#420"
msgid "HDMI"
msgstr ""
-#empty string with id 421
+#: system/settings/settings.xml
+msgctxt "#421"
+msgid "Stream silence when idle"
+msgstr ""
msgctxt "#422"
msgid "Delete album info"
@@ -12125,8 +12128,13 @@ msgctxt "#34110"
msgid "7.1"
msgstr ""
-#empty strings from id 34111 to 34119
-#34111-34119 reserved for future use
+#: system/settings/settings.xml
+msgctxt "#34111"
+msgid "When activated silence is output in order to keep alive receiver, otherwise sink is drained in idle state"
+msgstr ""
+
+#empty strings from id 34112 to 34119
+#34112-34119 reserved for future use
#: system/settings/settings.xml
msgctxt "#34120"
@@ -12890,7 +12898,7 @@ msgstr ""
#: system/settings/settings.xml
msgctxt "#36169"
-msgid "No info available yet."
+msgid "Resampling and other sound processing quality. Low quality is fast, higher quality will consume more CPU."
msgstr ""
#: system/settings/settings.xml
diff --git a/lib/DllAvUtil.h b/lib/DllAvUtil.h
index 1afee5e367..521263f7d9 100644
--- a/lib/DllAvUtil.h
+++ b/lib/DllAvUtil.h
@@ -82,6 +82,8 @@ public:
virtual const AVCRC* av_crc_get_table(AVCRCId crc_id)=0;
virtual uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)=0;
virtual int av_opt_set(void *obj, const char *name, const char *val, int search_flags)=0;
+ virtual int av_opt_set_double(void *obj, const char *name, double val, int search_flags)=0;
+ virtual int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)=0;
virtual AVFifoBuffer *av_fifo_alloc(unsigned int size) = 0;
virtual void av_fifo_free(AVFifoBuffer *f) = 0;
virtual void av_fifo_reset(AVFifoBuffer *f) = 0;
@@ -95,6 +97,9 @@ public:
virtual void av_dict_free(AVDictionary **pm) = 0;
virtual int av_samples_get_buffer_size (int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align) = 0;
virtual int64_t av_get_default_channel_layout(int nb_channels)=0;
+ virtual int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align) = 0;
+ virtual int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt) = 0;
+ virtual int av_get_channel_layout_channel_index (uint64_t channel_layout, uint64_t channel) = 0;
};
#if defined (USE_EXTERNAL_FFMPEG) || (defined TARGET_DARWIN)
@@ -116,6 +121,8 @@ public:
virtual const AVCRC* av_crc_get_table(AVCRCId crc_id) { return ::av_crc_get_table(crc_id); }
virtual uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length) { return ::av_crc(ctx, crc, buffer, length); }
virtual int av_opt_set(void *obj, const char *name, const char *val, int search_flags) { return ::av_opt_set(obj, name, val, search_flags); }
+ virtual int av_opt_set_double(void *obj, const char *name, double val, int search_flags) { return ::av_opt_set_double(obj, name, val, search_flags); }
+ virtual int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags) { return ::av_opt_set_int(obj, name, val, search_flags); }
virtual AVFifoBuffer *av_fifo_alloc(unsigned int size) {return ::av_fifo_alloc(size); }
virtual void av_fifo_free(AVFifoBuffer *f) { ::av_fifo_free(f); }
virtual void av_fifo_reset(AVFifoBuffer *f) { ::av_fifo_reset(f); }
@@ -133,6 +140,10 @@ public:
virtual int av_samples_get_buffer_size (int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align)
{ return ::av_samples_get_buffer_size(linesize, nb_channels, nb_samples, sample_fmt, align); }
virtual int64_t av_get_default_channel_layout(int nb_channels) { return ::av_get_default_channel_layout(nb_channels); }
+ virtual int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align)
+ { return ::av_samples_alloc(audio_data, linesize, nb_channels, nb_samples, sample_fmt, align); }
+ virtual int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt) { return ::av_sample_fmt_is_planar(sample_fmt); }
+ virtual int av_get_channel_layout_channel_index (uint64_t channel_layout, uint64_t channel) { return ::av_get_channel_layout_channel_index(channel_layout, channel); }
// DLL faking.
virtual bool ResolveExports() { return true; }
@@ -165,6 +176,8 @@ class DllAvUtilBase : public DllDynamic, DllAvUtilInterface
DEFINE_METHOD5(int, av_crc_init, (AVCRC *p1, int p2, int p3, uint32_t p4, int p5));
DEFINE_METHOD4(uint32_t, av_crc, (const AVCRC *p1, uint32_t p2, const uint8_t *p3, size_t p4));
DEFINE_METHOD4(int, av_opt_set, (void *p1, const char *p2, const char *p3, int p4));
+ DEFINE_METHOD4(int, av_opt_set_double, (void *p1, const char *p2, double p3, int p4))
+ DEFINE_METHOD4(int, av_opt_set_int, (void *p1, const char *p2, int64_t p3, int p4))
DEFINE_METHOD1(AVFifoBuffer*, av_fifo_alloc, (unsigned int p1))
DEFINE_METHOD1(void, av_fifo_free, (AVFifoBuffer *p1))
DEFINE_METHOD1(void, av_fifo_reset, (AVFifoBuffer *p1))
@@ -178,6 +191,9 @@ class DllAvUtilBase : public DllDynamic, DllAvUtilInterface
DEFINE_METHOD1(void, av_dict_free, (AVDictionary **p1));
DEFINE_METHOD5(int, av_samples_get_buffer_size, (int *p1, int p2, int p3, enum AVSampleFormat p4, int p5))
DEFINE_METHOD1(int64_t, av_get_default_channel_layout, (int p1))
+ DEFINE_METHOD6(int, av_samples_alloc, (uint8_t **p1, int *p2, int p3, int p4, enum AVSampleFormat p5, int p6))
+ DEFINE_METHOD1(int, av_sample_fmt_is_planar, (enum AVSampleFormat p1))
+ DEFINE_METHOD2(int, av_get_channel_layout_channel_index, (uint64_t p1, uint64_t p2))
public:
BEGIN_METHOD_RESOLVE()
@@ -193,6 +209,8 @@ class DllAvUtilBase : public DllDynamic, DllAvUtilInterface
RESOLVE_METHOD(av_crc_get_table)
RESOLVE_METHOD(av_crc)
RESOLVE_METHOD(av_opt_set)
+ RESOLVE_METHOD(av_opt_set_double)
+ RESOLVE_METHOD(av_opt_set_int)
RESOLVE_METHOD(av_fifo_alloc)
RESOLVE_METHOD(av_fifo_free)
RESOLVE_METHOD(av_fifo_reset)
@@ -206,6 +224,10 @@ class DllAvUtilBase : public DllDynamic, DllAvUtilInterface
RESOLVE_METHOD(av_dict_free)
RESOLVE_METHOD(av_samples_get_buffer_size)
RESOLVE_METHOD(av_get_default_channel_layout)
+ RESOLVE_METHOD(av_samples_alloc)
+ RESOLVE_METHOD(av_sample_fmt_is_planar)
+ RESOLVE_METHOD(av_get_channel_layout_channel_index)
+
END_METHOD_RESOLVE()
};
diff --git a/lib/DllSwResample.h b/lib/DllSwResample.h
index e9613d35f4..57eeda133e 100644
--- a/lib/DllSwResample.h
+++ b/lib/DllSwResample.h
@@ -60,6 +60,9 @@ public:
virtual int swr_init(struct SwrContext *s)=0;
virtual void swr_free(struct SwrContext **s)=0;
virtual int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in , int in_count)=0;
+ virtual int64_t swr_get_delay(struct SwrContext *s, int64_t base) = 0;
+ virtual int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map) = 0;
+ virtual int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride) = 0;
};
#if (defined USE_EXTERNAL_FFMPEG) || (defined TARGET_DARWIN)
@@ -84,6 +87,9 @@ public:
virtual int swr_init(struct SwrContext *s) { return ::swr_init(s); }
virtual void swr_free(struct SwrContext **s){ return ::swr_free(s); }
virtual int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in , int in_count){ return ::swr_convert(s, out, out_count, in, in_count); }
+ virtual int64_t swr_get_delay(struct SwrContext *s, int64_t base) { return ::swr_get_delay(s, base); }
+ virtual int swr_set_channel_mapping (struct SwrContext *s, const int *channel_map) { return ::swr_set_channel_mapping(s, channel_map); }
+ virtual int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride) { return ::swr_set_matrix(s, matrix, stride); }
};
#else
// Wrap the same API through libavresample.
@@ -129,12 +135,18 @@ class DllSwResample : public DllDynamic, DllSwResampleInterface
DEFINE_METHOD1(int, swr_init, (struct SwrContext *p1))
DEFINE_METHOD1(void, swr_free, (struct SwrContext **p1))
DEFINE_METHOD5(int, swr_convert, (struct SwrContext *p1, uint8_t **p2, int p3, const uint8_t **p4, int p5))
+ DEFINE_METHOD2(int64_t, swr_get_delay, (struct SwrContext *p1, int64_t p2))
+ DEFINE_METHOD2(int, swr_set_channel_mapping, (struct SwrContext *p1, const int *p2))
+ DEFINE_METHOD3(int, swr_set_matrix, (struct SwrContext *p1, const double *p2, int p3))
BEGIN_METHOD_RESOLVE()
RESOLVE_METHOD(swr_alloc_set_opts)
RESOLVE_METHOD(swr_init)
RESOLVE_METHOD(swr_free)
RESOLVE_METHOD(swr_convert)
+ RESOLVE_METHOD(swr_get_delay)
+ RESOLVE_METHOD(swr_set_channel_mapping)
+ RESOLVE_METHOD(swr_set_matrix)
END_METHOD_RESOLVE()
/* dependencies of libavformat */
diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj
index 90200d46d9..e06fb25bdc 100644
--- a/project/VS2010Express/XBMC.vcxproj
+++ b/project/VS2010Express/XBMC.vcxproj
@@ -379,6 +379,12 @@
<ClCompile Include="..\..\xbmc\cores\AudioEngine\AEFactory.cpp" />
<ClCompile Include="..\..\xbmc\cores\AudioEngine\AESinkFactory.cpp" />
<ClCompile Include="..\..\xbmc\cores\AudioEngine\Encoders\AEEncoderFFmpeg.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAE.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEBuffer.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEResample.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESink.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESound.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEStream.cpp" />
<ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE\SoftAE.cpp" />
<ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE\SoftAESound.cpp" />
<ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE\SoftAEStream.cpp" />
@@ -1031,6 +1037,12 @@
<ClInclude Include="..\..\xbmc\cores\AudioEngine\AEFactory.h" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\AESinkFactory.h" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\Encoders\AEEncoderFFmpeg.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAE.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEBuffer.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEResample.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESink.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESound.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEStream.h" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE\SoftAE.h" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE\SoftAESound.h" />
<ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE\SoftAEStream.h" />
@@ -1177,6 +1189,7 @@
<ClInclude Include="..\..\xbmc\settings\windows\GUIWindowSettingsCategory.h" />
<ClInclude Include="..\..\xbmc\settings\windows\GUIWindowSettingsScreenCalibration.h" />
<ClInclude Include="..\..\xbmc\settings\windows\GUIWindowTestPattern.h" />
+ <ClInclude Include="..\..\xbmc\utils\ActorProtocol.h" />
<ClInclude Include="..\..\xbmc\utils\BooleanLogic.h" />
<ClInclude Include="..\..\xbmc\utils\IRssObserver.h" />
<ClInclude Include="..\..\xbmc\utils\IXmlDeserializable.h" />
@@ -1319,6 +1332,7 @@
</ClInclude>
<ClInclude Include="..\..\xbmc\interfaces\json-rpc\AddonsOperations.h" />
<ClCompile Include="..\..\xbmc\ThumbLoader.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\ActorProtocol.cpp" />
<ClCompile Include="..\..\xbmc\utils\BooleanLogic.cpp" />
<ClCompile Include="..\..\xbmc\utils\LegacyPathTranslation.cpp" />
<ClCompile Include="..\..\xbmc\utils\RssManager.cpp" />
@@ -2976,4 +2990,4 @@
</VisualStudio>
</ProjectExtensions>
<Import Project="$(SolutionDir)\$(ProjectFileName).targets.user" Condition="Exists('$(SolutionDir)\$(ProjectFileName).targets.user')" />
-</Project>
+</Project> \ No newline at end of file
diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters
index f989167d0f..3b2c73da82 100644
--- a/project/VS2010Express/XBMC.vcxproj.filters
+++ b/project/VS2010Express/XBMC.vcxproj.filters
@@ -307,6 +307,9 @@
<Filter Include="interfaces\generic">
<UniqueIdentifier>{4286258a-45d7-45e8-9e56-ebf18fea53ec}</UniqueIdentifier>
</Filter>
+ <Filter Include="cores\AudioEngine\Engines\ActiveAE">
+ <UniqueIdentifier>{27f2c647-7b5f-4c49-b2e7-22bf360e58ab}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\xbmc\win32\pch.cpp">
@@ -3045,6 +3048,27 @@
<Filter>addons</Filter>
</ClCompile>
<ClCompile Include="..\..\xbmc\GitRevision.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAE.cpp">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEBuffer.cpp">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEResample.cpp">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESink.cpp">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESound.cpp">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEStream.cpp">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\utils\ActorProtocol.cpp">
+ <Filter>utils</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\xbmc\win32\pch.h">
@@ -5972,6 +5996,27 @@
<Filter>addons</Filter>
</ClInclude>
<ClInclude Include="..\..\xbmc\win32\PlatformInclude.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAE.h">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEBuffer.h">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEResample.h">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESink.h">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAESound.h">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\ActiveAE\ActiveAEStream.h">
+ <Filter>cores\AudioEngine\Engines\ActiveAE</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\utils\ActorProtocol.h">
+ <Filter>utils</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
@@ -6001,4 +6046,4 @@
<Filter>interfaces\swig</Filter>
</None>
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index d5e05ac071..32573dcfd0 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -1959,6 +1959,15 @@
<level>2</level>
<default>true</default>
</setting>
+ <setting id="audiooutput.processquality" type="integer" label="13505" help="36169">
+ <visible>HAS_AE_QUALITY_LEVELS</visible>
+ <level>2</level>
+ <default>30</default> <!-- AE_QUALITY_MID -->
+ <constraints>
+ <options>aequalitylevels</options>
+ </constraints>
+ <control type="spinner" format="string" />
+ </setting>
<setting id="audiooutput.stereoupmix" type="boolean" label="252" help="36364">
<level>2</level>
<default>false</default>
@@ -2025,6 +2034,11 @@
</dependency>
</dependencies>
</setting>
+ <setting id="audiooutput.streamsilence" type="boolean" label="421" help="34111">
+ <level>2</level>
+ <visible>audiosupportsdrain</visible>
+ <default>true</default>
+ </setting>
</group>
<group id="2">
<setting id="audiooutput.audiodevice" type="string" label="545" help="36371">
diff --git a/system/settings/win32.xml b/system/settings/win32.xml
index 76bc2bf989..1e0a97bc80 100644
--- a/system/settings/win32.xml
+++ b/system/settings/win32.xml
@@ -39,5 +39,33 @@
</setting>
</group>
</category>
+ <category id="audiooutput" label="772" help="36360">
+ <group id="2">
+ <setting id="audiooutput.audiodevice" type="string" label="545" help="36371">
+ <level>2</level>
+ <default>WASAPI:default</default> <!-- will be properly set on startup -->
+ <constraints>
+ <options>audiodevices</options>
+ </constraints>
+ <control type="spinner" format="string" />
+ </setting>
+ <setting id="audiooutput.passthroughdevice" type="string" label="546" help="36372">
+ <level>2</level>
+ <default>WASAPI:default</default> <!-- will be properly set on startup -->
+ <constraints>
+ <options>audiodevicespassthrough</options>
+ </constraints>
+ <dependencies>
+ <dependency type="enable">
+ <or>
+ <condition setting="audiooutput.mode">1</condition> <!-- AUDIO_IEC958 -->
+ <condition setting="audiooutput.mode">2</condition> <!-- AUDIO_HDMI -->
+ </or>
+ </dependency>
+ </dependencies>
+ <control type="spinner" format="string" />
+ </setting>
+ </group>
+ </category>
</section>
</settings>
diff --git a/xbmc/addons/Visualisation.cpp b/xbmc/addons/Visualisation.cpp
index 44c7bba548..8ac8ca9992 100644
--- a/xbmc/addons/Visualisation.cpp
+++ b/xbmc/addons/Visualisation.cpp
@@ -111,6 +111,7 @@ bool CVisualisation::Create(int x, int y, int w, int h, void *device)
if (g_application.m_pPlayer)
g_application.m_pPlayer->RegisterAudioCallback(this);
+ CAEFactory::RegisterAudioCallback(this);
return true;
}
@@ -175,6 +176,7 @@ void CVisualisation::Render()
void CVisualisation::Stop()
{
if (g_application.m_pPlayer) g_application.m_pPlayer->UnRegisterAudioCallback();
+ CAEFactory::UnregisterAudioCallback();
if (Initialized())
{
CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Stop();
diff --git a/xbmc/cores/AudioEngine/AEAudioFormat.h b/xbmc/cores/AudioEngine/AEAudioFormat.h
index 369811c038..64be36f794 100644
--- a/xbmc/cores/AudioEngine/AEAudioFormat.h
+++ b/xbmc/cores/AudioEngine/AEAudioFormat.h
@@ -31,26 +31,33 @@ enum AEDataFormat
AE_FMT_INVALID = -1,
AE_FMT_U8,
+ AE_FMT_U8P,
AE_FMT_S8,
AE_FMT_S16BE,
AE_FMT_S16LE,
AE_FMT_S16NE,
+ AE_FMT_S16NEP,
AE_FMT_S32BE,
AE_FMT_S32LE,
AE_FMT_S32NE,
+ AE_FMT_S32NEP,
AE_FMT_S24BE4,
AE_FMT_S24LE4,
AE_FMT_S24NE4, /* S24 in 4 bytes */
+ AE_FMT_S24NE4P,
AE_FMT_S24BE3,
AE_FMT_S24LE3,
AE_FMT_S24NE3, /* S24 in 3 bytes */
+ AE_FMT_S24NE3P,
AE_FMT_DOUBLE,
+ AE_FMT_DOUBLEP,
AE_FMT_FLOAT,
+ AE_FMT_FLOATP,
/* Bitstream formats */
AE_FMT_AAC,
diff --git a/xbmc/cores/AudioEngine/AEFactory.cpp b/xbmc/cores/AudioEngine/AEFactory.cpp
index 7141dd34d0..50a309c22c 100644
--- a/xbmc/cores/AudioEngine/AEFactory.cpp
+++ b/xbmc/cores/AudioEngine/AEFactory.cpp
@@ -27,6 +27,7 @@
#include "settings/SettingsManager.h"
#else
#include "Engines/SoftAE/SoftAE.h"
+ #include "Engines/ActiveAE/ActiveAE.h"
#endif
#if defined(HAS_PULSEAUDIO)
@@ -67,6 +68,22 @@ bool CAEFactory::LoadEngine()
#endif
if (!loaded && engine == "SOFT" )
loaded = CAEFactory::LoadEngine(AE_ENGINE_SOFT);
+ if (!loaded && engine == "ACTIVE")
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_ACTIVE);
+ }
+#endif
+
+#if defined(TARGET_WINDOWS)
+ std::string engine;
+ if (getenv("AE_ENGINE"))
+ {
+ engine = (std::string)getenv("AE_ENGINE");
+ std::transform(engine.begin(), engine.end(), engine.begin(), ::toupper);
+
+ if (!loaded && engine == "SOFT" )
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_SOFT);
+ if (!loaded && engine == "ACTIVE")
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_ACTIVE);
}
#endif
@@ -99,6 +116,7 @@ bool CAEFactory::LoadEngine(enum AEEngine engine)
case AE_ENGINE_COREAUDIO: AE = new CCoreAudioAE(); break;
#else
case AE_ENGINE_SOFT : AE = new CSoftAE(); break;
+ case AE_ENGINE_ACTIVE : AE = new ActiveAE::CActiveAE(); break;
#endif
#if defined(HAS_PULSEAUDIO)
case AE_ENGINE_PULSE : AE = new CPulseAE(); break;
@@ -243,6 +261,28 @@ bool CAEFactory::SupportsRaw()
return false;
}
+bool CAEFactory::SupportsDrain()
+{
+ if(AE)
+ return AE->SupportsDrain();
+
+ return false;
+}
+
+/**
+ * Returns true if current AudioEngine supports at lest two basic quality levels
+ * @return true if quality setting is supported, otherwise false
+ */
+bool CAEFactory::SupportsQualitySetting(void)
+{
+ if (!AE)
+ return false;
+
+ return ((AE->SupportsQualityLevel(AE_QUALITY_LOW)? 1 : 0) +
+ (AE->SupportsQualityLevel(AE_QUALITY_MID)? 1 : 0) +
+ (AE->SupportsQualityLevel(AE_QUALITY_HIGH)? 1 : 0)) >= 2;
+}
+
void CAEFactory::SetMute(const bool enabled)
{
if(AE)
@@ -323,6 +363,21 @@ void CAEFactory::SettingOptionsAudioOutputModesFiller(const CSetting *setting, s
list.push_back(std::make_pair(g_localizeStrings.Get(420), AUDIO_HDMI));
}
+void CAEFactory::SettingOptionsAudioQualityLevelsFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int &current)
+{
+ if (!AE)
+ return;
+
+ if(AE->SupportsQualityLevel(AE_QUALITY_LOW))
+ list.push_back(std::make_pair(g_localizeStrings.Get(13506), AE_QUALITY_LOW));
+ if(AE->SupportsQualityLevel(AE_QUALITY_MID))
+ list.push_back(std::make_pair(g_localizeStrings.Get(13507), AE_QUALITY_MID));
+ if(AE->SupportsQualityLevel(AE_QUALITY_HIGH))
+ list.push_back(std::make_pair(g_localizeStrings.Get(13508), AE_QUALITY_HIGH));
+ if(AE->SupportsQualityLevel(AE_QUALITY_REALLYHIGH))
+ list.push_back(std::make_pair(g_localizeStrings.Get(13509), AE_QUALITY_REALLYHIGH));
+}
+
void CAEFactory::SettingOptionsAudioDevicesFillerGeneral(const CSetting *setting, std::vector< std::pair<std::string, std::string> > &list, std::string &current, bool passthrough)
{
current = ((const CSettingString*)setting)->GetValue();
@@ -358,3 +413,15 @@ void CAEFactory::SettingOptionsAudioDevicesFillerGeneral(const CSetting *setting
if (!foundValue)
current = firstDevice;
}
+
+void CAEFactory::RegisterAudioCallback(IAudioCallback* pCallback)
+{
+ if (AE)
+ AE->RegisterAudioCallback(pCallback);
+}
+
+void CAEFactory::UnregisterAudioCallback()
+{
+ if (AE)
+ AE->UnregisterAudioCallback();
+}
diff --git a/xbmc/cores/AudioEngine/AEFactory.h b/xbmc/cores/AudioEngine/AEFactory.h
index 56a1e273f3..ed345b6140 100644
--- a/xbmc/cores/AudioEngine/AEFactory.h
+++ b/xbmc/cores/AudioEngine/AEFactory.h
@@ -31,7 +31,8 @@ enum AEEngine
AE_ENGINE_NULL,
AE_ENGINE_SOFT,
AE_ENGINE_COREAUDIO,
- AE_ENGINE_PULSE
+ AE_ENGINE_PULSE,
+ AE_ENGINE_ACTIVE
};
class CAEFactory
@@ -53,6 +54,13 @@ public:
static void VerifyOutputDevice(std::string &device, bool passthrough);
static std::string GetDefaultDevice(bool passthrough);
static bool SupportsRaw();
+ static bool SupportsDrain();
+
+ /**
+ * Returns true if current AudioEngine supports at lest two basic quality levels
+ * @return true if quality setting is supported, otherwise false
+ */
+ static bool SupportsQualitySetting(void);
static void SetMute(const bool enabled);
static bool IsMuted();
static float GetVolume();
@@ -66,6 +74,10 @@ public:
static void SettingOptionsAudioDevicesFiller(const CSetting *setting, std::vector< std::pair<std::string, std::string> > &list, std::string &current);
static void SettingOptionsAudioDevicesPassthroughFiller(const CSetting *setting, std::vector< std::pair<std::string, std::string> > &list, std::string &current);
static void SettingOptionsAudioOutputModesFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int &current);
+ static void SettingOptionsAudioQualityLevelsFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int &current);
+
+ static void RegisterAudioCallback(IAudioCallback* pCallback);
+ static void UnregisterAudioCallback();
private:
static bool LoadEngine(enum AEEngine engine);
diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
index ba89868d64..2f305b12a9 100644
--- a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
+++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
@@ -98,7 +98,7 @@ unsigned int CAEEncoderFFmpeg::BuildChannelLayout(const int64_t ffmap, CAEChanne
return layout.Count();
}
-bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
+bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format, bool allow_planar_input)
{
Reset();
@@ -150,6 +150,7 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
bool hasS32 = false;
bool hasS16 = false;
bool hasU8 = false;
+ bool hasFloatP = false;
bool hasUnknownFormat = false;
for(int i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i)
@@ -161,6 +162,12 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
case AV_SAMPLE_FMT_S32: hasS32 = true; break;
case AV_SAMPLE_FMT_S16: hasS16 = true; break;
case AV_SAMPLE_FMT_U8 : hasU8 = true; break;
+ case AV_SAMPLE_FMT_FLTP:
+ if (allow_planar_input)
+ hasFloatP = true;
+ else
+ hasUnknownFormat = true;
+ break;
case AV_SAMPLE_FMT_NONE: return false;
default: hasUnknownFormat = true; break;
}
@@ -171,6 +178,11 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_FLT;
format.m_dataFormat = AE_FMT_FLOAT;
}
+ else if (hasFloatP)
+ {
+ m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
+ format.m_dataFormat = AE_FMT_FLOATP;
+ }
else if (hasDouble)
{
m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_DBL;
@@ -214,7 +226,6 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
return false;
}
- format.m_dataFormat = AE_FMT_FLOAT;
format.m_frames = m_CodecCtx->frame_size;
format.m_frameSamples = m_CodecCtx->frame_size * m_CodecCtx->channels;
format.m_frameSize = m_CodecCtx->channels * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
@@ -351,6 +362,57 @@ int CAEEncoderFFmpeg::Encode(float *data, unsigned int frames)
return m_NeededFrames;
}
+int CAEEncoderFFmpeg::Encode(uint8_t *in, int in_size, uint8_t *out, int out_size)
+{
+ int got_output;
+ AVFrame *frame;
+
+ if (!m_CodecCtx)
+ return 0;
+
+ /* allocate the input frame
+ * sadly, we have to alloc/dealloc it everytime since we have no guarantee the
+ * data argument will be constant over iterated calls and the frame needs to
+ * setup pointers inside data */
+ frame = m_dllAvCodec.avcodec_alloc_frame();
+ if (!frame)
+ return 0;
+
+ frame->nb_samples = m_CodecCtx->frame_size;
+ frame->format = m_CodecCtx->sample_fmt;
+ frame->channel_layout = m_CodecCtx->channel_layout;
+
+ m_dllAvCodec.avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt,
+ in, in_size, 0);
+
+ /* initialize the output packet */
+ m_dllAvCodec.av_init_packet(&m_Pkt);
+ m_Pkt.size = out_size - IEC61937_DATA_OFFSET;
+ m_Pkt.data = out + IEC61937_DATA_OFFSET;
+
+ /* encode it */
+ int ret = m_dllAvCodec.avcodec_encode_audio2(m_CodecCtx, &m_Pkt, frame, &got_output);
+
+ /* free temporary data */
+ m_dllAvCodec.avcodec_free_frame(&frame);
+
+ if (ret < 0 || !got_output)
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Encoding failed");
+ return 0;
+ }
+
+ /* pack it into an IEC958 frame */
+ m_PackFunc(NULL, m_Pkt.size, out);
+
+ /* free the packet */
+ m_dllAvCodec.av_free_packet(&m_Pkt);
+
+ /* return the number of frames used */
+ return m_NeededFrames;
+}
+
+
int CAEEncoderFFmpeg::GetData(uint8_t **data)
{
int size;
diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h
index 4ecc9f2951..4fb67c4525 100644
--- a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h
+++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h
@@ -36,7 +36,7 @@ public:
virtual ~CAEEncoderFFmpeg();
virtual bool IsCompatible(AEAudioFormat format);
- virtual bool Initialize(AEAudioFormat &format);
+ virtual bool Initialize(AEAudioFormat &format, bool allow_planar_input = false);
virtual void Reset();
virtual unsigned int GetBitRate ();
@@ -44,6 +44,7 @@ public:
virtual unsigned int GetFrames ();
virtual int Encode (float *data, unsigned int frames);
+ virtual int Encode (uint8_t *in, int in_size, uint8_t *out, int out_size);
virtual int GetData(uint8_t **data);
virtual double GetDelay(unsigned int bufferSize);
private:
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
new file mode 100644
index 0000000000..31aa3c0a53
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
@@ -0,0 +1,2311 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ActiveAE.h"
+
+using namespace ActiveAE;
+#include "ActiveAESound.h"
+#include "ActiveAEStream.h"
+#include "Utils/AEUtil.h"
+#include "Encoders/AEEncoderFFmpeg.h"
+
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+#include "windowing/WindowingFactory.h"
+
+#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
+
+void CEngineStats::Reset(unsigned int sampleRate)
+{
+ CSingleLock lock(m_lock);
+ m_sinkUpdate = XbmcThreads::SystemClockMillis();
+ m_sinkDelay = 0;
+ m_sinkSampleRate = sampleRate;
+ m_bufferedSamples = 0;
+ m_suspended = false;
+}
+
+void CEngineStats::UpdateSinkDelay(double delay, int samples)
+{
+ CSingleLock lock(m_lock);
+ m_sinkUpdate = XbmcThreads::SystemClockMillis();
+ m_sinkDelay = delay;
+ if (samples > m_bufferedSamples)
+ {
+ CLog::Log(LOGERROR, "CEngineStats::UpdateSinkDelay - inconsistency in buffer time");
+ }
+ else
+ m_bufferedSamples -= samples;
+}
+
+void CEngineStats::AddSamples(int samples, std::list<CActiveAEStream*> &streams)
+{
+ CSingleLock lock(m_lock);
+ m_bufferedSamples += samples;
+
+ //update buffered time of streams
+ std::list<CActiveAEStream*>::iterator it;
+ for(it=streams.begin(); it!=streams.end(); ++it)
+ {
+ float delay = 0;
+ std::deque<CSampleBuffer*>::iterator itBuf;
+ for(itBuf=(*it)->m_processingSamples.begin(); itBuf!=(*it)->m_processingSamples.end(); ++itBuf)
+ {
+ delay += (float)(*itBuf)->pkt->nb_samples / (*itBuf)->pkt->config.sample_rate;
+ }
+ delay += (*it)->m_resampleBuffers->GetDelay();
+ (*it)->m_bufferedTime = delay;
+ }
+}
+
+float CEngineStats::GetDelay()
+{
+ CSingleLock lock(m_lock);
+ unsigned int now = XbmcThreads::SystemClockMillis();
+ float delay = m_sinkDelay - (double)(now-m_sinkUpdate) / 1000;
+ delay += (float)m_bufferedSamples / m_sinkSampleRate;
+
+ return delay;
+}
+
+float CEngineStats::GetDelay(CActiveAEStream *stream)
+{
+ CSingleLock lock(m_lock);
+ unsigned int now = XbmcThreads::SystemClockMillis();
+ float delay = m_sinkDelay - (double)(now-m_sinkUpdate) / 1000;
+ delay += (float)m_bufferedSamples / m_sinkSampleRate;
+
+ delay += stream->m_bufferedTime;
+ return delay;
+}
+
+float CEngineStats::GetCacheTime(CActiveAEStream *stream)
+{
+ CSingleLock lock(m_lock);
+ float delay = (float)m_bufferedSamples / m_sinkSampleRate;
+
+ delay += stream->m_bufferedTime;
+ return delay;
+}
+
+float CEngineStats::GetCacheTotal(CActiveAEStream *stream)
+{
+ return MAX_CACHE_LEVEL + m_sinkCacheTotal;
+}
+
+float CEngineStats::GetWaterLevel()
+{
+ return (float)m_bufferedSamples / m_sinkSampleRate;
+}
+
+void CEngineStats::SetSuspended(bool state)
+{
+ CSingleLock lock(m_lock);
+ m_suspended = state;
+}
+
+bool CEngineStats::IsSuspended()
+{
+ CSingleLock lock(m_lock);
+ return m_suspended;
+}
+
+CActiveAE::CActiveAE() :
+ CThread("ActiveAE"),
+ m_controlPort("OutputControlPort", &m_inMsgEvent, &m_outMsgEvent),
+ m_dataPort("OutputDataPort", &m_inMsgEvent, &m_outMsgEvent),
+ m_sink(&m_outMsgEvent)
+{
+ m_sinkBuffers = NULL;
+ m_silenceBuffers = NULL;
+ m_encoderBuffers = NULL;
+ m_vizBuffers = NULL;
+ m_volume = 1.0;
+ m_aeVolume = 1.0;
+ m_muted = false;
+ m_aeMuted = false;
+ m_mode = MODE_PCM;
+ m_encoder = NULL;
+ m_audioCallback = NULL;
+ m_vizInitialized = false;
+ m_sinkHasVolume = false;
+}
+
+CActiveAE::~CActiveAE()
+{
+ Dispose();
+}
+
+void CActiveAE::Dispose()
+{
+#if defined(HAS_GLX) || defined(TARGET_DARWIN_OSX)
+ g_Windowing.Unregister(this);
+#endif
+
+ m_bStop = true;
+ m_outMsgEvent.Set();
+ StopThread();
+ m_controlPort.Purge();
+ m_dataPort.Purge();
+ m_sink.Dispose();
+
+ m_dllAvFormat.Unload();
+ m_dllAvCodec.Unload();
+ m_dllAvUtil.Unload();
+}
+
+//-----------------------------------------------------------------------------
+// Behavior
+//-----------------------------------------------------------------------------
+
+enum AE_STATES
+{
+ AE_TOP = 0, // 0
+ AE_TOP_ERROR, // 1
+ AE_TOP_UNCONFIGURED, // 2
+ AE_TOP_RECONFIGURING, // 3
+ AE_TOP_CONFIGURED, // 4
+ AE_TOP_CONFIGURED_SUSPEND, // 5
+ AE_TOP_CONFIGURED_IDLE, // 6
+ AE_TOP_CONFIGURED_PLAY, // 7
+};
+
+int AE_parentStates[] = {
+ -1,
+ 0, //TOP_ERROR
+ 0, //TOP_UNCONFIGURED
+ 0, //TOP_CONFIGURED
+ 0, //TOP_RECONFIGURING
+ 4, //TOP_CONFIGURED_SUSPEND
+ 4, //TOP_CONFIGURED_IDLE
+ 4, //TOP_CONFIGURED_PLAY
+};
+
+void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
+{
+ for (int state = m_state; ; state = AE_parentStates[state])
+ {
+ switch (state)
+ {
+ case AE_TOP: // TOP
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::GETSTATE:
+ msg->Reply(CActiveAEControlProtocol::ACC, &m_state, sizeof(m_state));
+ return;
+ case CActiveAEControlProtocol::SOUNDMODE:
+ m_soundMode = *(int*)msg->data;
+ return;
+ case CActiveAEControlProtocol::VOLUME:
+ m_volume = *(float*)msg->data;
+ if (m_sinkHasVolume)
+ m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::VOLUME, &m_volume, sizeof(float));
+ return;
+ case CActiveAEControlProtocol::MUTE:
+ m_muted = *(bool*)msg->data;
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case CActiveAEDataProtocol::NEWSOUND:
+ CActiveAESound *sound;
+ sound = *(CActiveAESound**)msg->data;
+ if (sound)
+ {
+ m_sounds.push_back(sound);
+ ResampleSounds();
+ }
+ return;
+ case CActiveAEDataProtocol::FREESTREAM:
+ CActiveAEStream *stream;
+ stream = *(CActiveAEStream**)msg->data;
+ DiscardStream(stream);
+ return;
+ case CActiveAEDataProtocol::FREESOUND:
+ sound = *(CActiveAESound**)msg->data;
+ DiscardSound(sound);
+ return;
+ case CActiveAEDataProtocol::DRAINSTREAM:
+ stream = *(CActiveAEStream**)msg->data;
+ stream->m_drain = true;
+ stream->m_resampleBuffers->m_drain = true;
+ msg->Reply(CActiveAEDataProtocol::ACC);
+ stream->m_streamPort->SendInMessage(CActiveAEDataProtocol::STREAMDRAINED);
+ return;
+ default:
+ break;
+ }
+ }
+ else if (&m_sink.m_dataPort)
+ {
+ switch (signal)
+ {
+ case CSinkDataProtocol::RETURNSAMPLE:
+ CSampleBuffer **buffer;
+ buffer = (CSampleBuffer**)msg->data;
+ if (buffer)
+ {
+ (*buffer)->Return();
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ {
+ std::string portName = port == NULL ? "timer" : port->portName;
+ CLog::Log(LOGWARNING, "CActiveAE::%s - signal: %d from port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
+ }
+ return;
+
+ case AE_TOP_ERROR:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::TIMEOUT:
+ m_extError = false;
+ LoadSettings();
+ Configure();
+ if (!m_extError)
+ {
+ m_state = AE_TOP_CONFIGURED_IDLE;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = AE_TOP_ERROR;
+ m_extTimeout = 500;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case AE_TOP_UNCONFIGURED:
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::INIT:
+ m_extError = false;
+ m_sink.EnumerateSinkList();
+ LoadSettings();
+ Configure();
+ msg->Reply(CActiveAEControlProtocol::ACC);
+ if (!m_extError)
+ {
+ m_state = AE_TOP_CONFIGURED_IDLE;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = AE_TOP_ERROR;
+ m_extTimeout = 500;
+ }
+ return;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ case AE_TOP_RECONFIGURING:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::TIMEOUT:
+ // drain
+ if (RunStages())
+ {
+ m_extTimeout = 0;
+ return;
+ }
+ if (!m_sinkBuffers->m_inputSamples.empty() || !m_sinkBuffers->m_outputSamples.empty())
+ {
+ m_extTimeout = 100;
+ return;
+ }
+ if (NeedReconfigureSink())
+ DrainSink();
+
+ if (!m_extError)
+ Configure();
+ if (!m_extError)
+ {
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = AE_TOP_ERROR;
+ m_extTimeout = 500;
+ }
+ m_extDeferData = false;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case AE_TOP_CONFIGURED:
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::RECONFIGURE:
+ if (m_streams.empty())
+ {
+ bool silence = false;
+ m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::SILENCEMODE, &silence, sizeof(bool));
+ }
+ LoadSettings();
+ ChangeResampleQuality();
+ if (!NeedReconfigureBuffers() && !NeedReconfigureSink())
+ return;
+ m_state = AE_TOP_RECONFIGURING;
+ m_extTimeout = 0;
+ // don't accept any data until we are reconfigured
+ m_extDeferData = true;
+ return;
+ case CActiveAEControlProtocol::SUSPEND:
+ UnconfigureSink();
+ m_stats.SetSuspended(true);
+ m_state = AE_TOP_CONFIGURED_SUSPEND;
+ m_extDeferData = true;
+ return;
+ case CActiveAEControlProtocol::DISPLAYLOST:
+ if (m_settings.mode == AUDIO_HDMI)
+ {
+ UnconfigureSink();
+ m_stats.SetSuspended(true);
+ m_state = AE_TOP_CONFIGURED_SUSPEND;
+ m_extDeferData = true;
+ }
+ return;
+ case CActiveAEControlProtocol::PAUSESTREAM:
+ CActiveAEStream *stream;
+ stream = *(CActiveAEStream**)msg->data;
+ stream->m_paused = true;
+ return;
+ case CActiveAEControlProtocol::RESUMESTREAM:
+ stream = *(CActiveAEStream**)msg->data;
+ stream->m_paused = false;
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ return;
+ case CActiveAEControlProtocol::STREAMAMP:
+ MsgStreamParameter *par;
+ par = (MsgStreamParameter*)msg->data;
+ par->stream->m_limiter.SetAmplification(par->parameter.float_par);
+ return;
+ case CActiveAEControlProtocol::STREAMVOLUME:
+ par = (MsgStreamParameter*)msg->data;
+ par->stream->m_volume = par->parameter.float_par;
+ return;
+ case CActiveAEControlProtocol::STREAMRGAIN:
+ par = (MsgStreamParameter*)msg->data;
+ par->stream->m_rgain = par->parameter.float_par;
+ return;
+ case CActiveAEControlProtocol::STREAMRESAMPLERATIO:
+ par = (MsgStreamParameter*)msg->data;
+ if (par->stream->m_resampleBuffers)
+ {
+ if ((unsigned int)(par->stream->m_resampleBuffers->m_format.m_sampleRate * par->parameter.double_par) != par->stream->m_resampleBuffers->m_outSampleRate)
+ {
+ par->stream->m_resampleBuffers->m_resampleRatio = par->parameter.double_par;
+ par->stream->m_resampleBuffers->m_resampleQuality = AE_QUALITY_LOW;
+ par->stream->m_resampleBuffers->m_changeResampler = true;
+ }
+ }
+ return;
+ case CActiveAEControlProtocol::STREAMFADE:
+ MsgStreamFade *fade;
+ fade = (MsgStreamFade*)msg->data;
+ fade->stream->m_fadingBase = fade->from;
+ fade->stream->m_fadingTarget = fade->target;
+ fade->stream->m_fadingTime = fade->millis;
+ fade->stream->m_fadingSamples = -1;
+ return;
+ case CActiveAEControlProtocol::STOPSOUND:
+ CActiveAESound *sound;
+ sound = *(CActiveAESound**)msg->data;
+ SStopSound(sound);
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case CActiveAEDataProtocol::PLAYSOUND:
+ CActiveAESound *sound;
+ sound = *(CActiveAESound**)msg->data;
+ if (m_soundMode == AE_SOUND_OFF ||
+ (m_soundMode == AE_SOUND_IDLE && !m_streams.empty()))
+ return;
+ if (sound)
+ {
+ SoundState st = {sound, 0};
+ m_sounds_playing.push_back(st);
+ m_extTimeout = 0;
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ }
+ return;
+ case CActiveAEDataProtocol::NEWSTREAM:
+ MsgStreamNew *streamMsg;
+ CActiveAEStream *stream;
+ streamMsg = (MsgStreamNew*)msg->data;
+ stream = CreateStream(streamMsg);
+ if(stream)
+ {
+ msg->Reply(CActiveAEDataProtocol::ACC, &stream, sizeof(CActiveAEStream*));
+ Configure();
+ if (!m_extError)
+ {
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = AE_TOP_ERROR;
+ m_extTimeout = 500;
+ }
+ }
+ else
+ msg->Reply(CActiveAEDataProtocol::ERR);
+ return;
+ case CActiveAEDataProtocol::STREAMSAMPLE:
+ MsgStreamSample *msgData;
+ CSampleBuffer *samples;
+ msgData = (MsgStreamSample*)msg->data;
+ samples = msgData->stream->m_processingSamples.front();
+ msgData->stream->m_processingSamples.pop_front();
+ if (samples != msgData->buffer)
+ CLog::Log(LOGERROR, "CActiveAE - inconsistency in stream sample message");
+ if (msgData->buffer->pkt->nb_samples == 0)
+ msgData->buffer->Return();
+ else
+ msgData->stream->m_resampleBuffers->m_inputSamples.push_back(msgData->buffer);
+ m_extTimeout = 0;
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ return;
+ case CActiveAEDataProtocol::FREESTREAM:
+ stream = *(CActiveAEStream**)msg->data;
+ DiscardStream(stream);
+ if (m_streams.empty())
+ {
+ m_extDrainTimer.Set(m_stats.GetDelay() * 1000);
+ m_extDrain = true;
+ }
+ m_extTimeout = 0;
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ return;
+ case CActiveAEDataProtocol::DRAINSTREAM:
+ stream = *(CActiveAEStream**)msg->data;
+ stream->m_drain = true;
+ stream->m_resampleBuffers->m_drain = true;
+ m_extTimeout = 0;
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ msg->Reply(CActiveAEDataProtocol::ACC);
+ return;
+ case CActiveAEDataProtocol::FLUSHSTREAM:
+ stream = *(CActiveAEStream**)msg->data;
+ SFlushStream(stream);
+ msg->Reply(CActiveAEDataProtocol::ACC);
+ return;
+ default:
+ break;
+ }
+ }
+ else if (&m_sink.m_dataPort)
+ {
+ switch (signal)
+ {
+ case CSinkDataProtocol::RETURNSAMPLE:
+ CSampleBuffer **buffer;
+ buffer = (CSampleBuffer**)msg->data;
+ if (buffer)
+ {
+ (*buffer)->Return();
+ }
+ m_extTimeout = 0;
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case AE_TOP_CONFIGURED_SUSPEND:
+ if (port == &m_controlPort)
+ {
+ bool displayReset = false;
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::DISPLAYRESET:
+ displayReset = true;
+ case CActiveAEControlProtocol::INIT:
+ m_extError = false;
+ if (!displayReset)
+ {
+ m_sink.EnumerateSinkList();
+ LoadSettings();
+ }
+ Configure();
+ if (!displayReset)
+ msg->Reply(CActiveAEControlProtocol::ACC);
+ if (!m_extError)
+ {
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = AE_TOP_ERROR;
+ m_extTimeout = 500;
+ }
+ m_stats.SetSuspended(false);
+ m_extDeferData = false;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case AE_TOP_CONFIGURED_IDLE:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::TIMEOUT:
+ ResampleSounds();
+ ClearDiscardedBuffers();
+ if (m_extDrain)
+ {
+ if (m_extDrainTimer.IsTimePast())
+ {
+ Configure();
+ if (!m_extError)
+ {
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = AE_TOP_ERROR;
+ m_extTimeout = 500;
+ }
+ }
+ else
+ m_extTimeout = m_extDrainTimer.MillisLeft();
+ }
+ else
+ m_extTimeout = 5000;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case AE_TOP_CONFIGURED_PLAY:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CActiveAEControlProtocol::TIMEOUT:
+ if (RunStages())
+ {
+ m_extTimeout = 0;
+ return;
+ }
+ if (!m_extDrain && HasWork())
+ {
+ ResampleSounds();
+ ClearDiscardedBuffers();
+ m_extTimeout = 100;
+ return;
+ }
+ m_extTimeout = 0;
+ m_state = AE_TOP_CONFIGURED_IDLE;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default: // we are in no state, should not happen
+ CLog::Log(LOGERROR, "CActiveAE::%s - no valid state: %d", __FUNCTION__, m_state);
+ return;
+ }
+ } // for
+}
+
+void CActiveAE::Process()
+{
+ Message *msg = NULL;
+ Protocol *port = NULL;
+ bool gotMsg;
+
+ m_state = AE_TOP_UNCONFIGURED;
+ m_extTimeout = 1000;
+ m_bStateMachineSelfTrigger = false;
+ m_extDrain = false;
+ m_extDeferData = false;
+
+ // start sink
+ m_sink.Start();
+
+ while (!m_bStop)
+ {
+ gotMsg = false;
+
+ if (m_bStateMachineSelfTrigger)
+ {
+ m_bStateMachineSelfTrigger = false;
+ // self trigger state machine
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ continue;
+ }
+ // check control port
+ else if (m_controlPort.ReceiveOutMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_controlPort;
+ }
+ // check sink data port
+ else if (m_sink.m_dataPort.ReceiveInMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_sink.m_dataPort;
+ }
+ else if (!m_extDeferData)
+ {
+ // check data port
+ if (m_dataPort.ReceiveOutMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_dataPort;
+ }
+ // stream data ports
+ else
+ {
+ std::list<CActiveAEStream*>::iterator it;
+ for(it=m_streams.begin(); it!=m_streams.end(); ++it)
+ {
+ if((*it)->m_streamPort->ReceiveOutMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_dataPort;
+ break;
+ }
+ }
+ }
+ }
+
+ if (gotMsg)
+ {
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ continue;
+ }
+
+ // wait for message
+ else if (m_outMsgEvent.WaitMSec(m_extTimeout))
+ {
+ continue;
+ }
+ // time out
+ else
+ {
+ msg = m_controlPort.GetMessage();
+ msg->signal = CActiveAEControlProtocol::TIMEOUT;
+ port = 0;
+ // signal timeout to state machine
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ }
+ }
+}
+
+void CActiveAE::Configure(AEAudioFormat *desiredFmt)
+{
+ bool initSink = false;
+ AEAudioFormat sinkInputFormat, inputFormat;
+ m_mode = MODE_PCM;
+
+ if (m_streams.empty())
+ {
+ inputFormat.m_dataFormat = AE_FMT_FLOAT;
+ inputFormat.m_sampleRate = 44100;
+ inputFormat.m_encodedRate = 0;
+ inputFormat.m_channelLayout = AE_CH_LAYOUT_2_0;
+ inputFormat.m_frames = 0;
+ inputFormat.m_frameSamples = 0;
+ inputFormat.m_frameSize = 0;
+ }
+ // force input format after unpausing slave
+ else if (desiredFmt != NULL)
+ {
+ inputFormat = *desiredFmt;
+ }
+ // keep format when having multiple streams
+ else if (m_streams.size() > 1 && m_silenceBuffers == NULL)
+ {
+ inputFormat = m_sinkRequestFormat;
+ }
+ else
+ {
+ inputFormat = m_streams.front()->m_format;
+ }
+
+ m_sinkRequestFormat = inputFormat;
+ ApplySettingsToFormat(m_sinkRequestFormat, m_settings, true);
+ std::string device = AE_IS_RAW(m_sinkRequestFormat.m_dataFormat) ? m_settings.passthoughdevice : m_settings.device;
+ std::string driver;
+ CAESinkFactory::ParseDevice(device, driver);
+ if (!m_sink.IsCompatible(m_sinkRequestFormat, device) || m_settings.driver.compare(driver) != 0)
+ {
+ if (!InitSink())
+ return;
+ m_settings.driver = driver;
+ initSink = true;
+ m_stats.Reset(m_sinkFormat.m_sampleRate);
+ m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::VOLUME, &m_volume, sizeof(float));
+ }
+
+ if (m_silenceBuffers)
+ {
+ m_discardBufferPools.push_back(m_silenceBuffers);
+ m_silenceBuffers = NULL;
+ }
+
+ // buffers for driving gui sounds if no streams are active
+ if (m_streams.empty())
+ {
+ inputFormat = m_sinkFormat;
+ inputFormat.m_dataFormat = AE_FMT_FLOAT;
+ inputFormat.m_frameSize = inputFormat.m_channelLayout.Count() *
+ (CAEUtil::DataFormatToBits(inputFormat.m_dataFormat) >> 3);
+ m_silenceBuffers = new CActiveAEBufferPool(inputFormat);
+ m_silenceBuffers->Create(MAX_WATER_LEVEL*1000);
+ sinkInputFormat = inputFormat;
+ m_internalFormat = inputFormat;
+
+ bool silence = false;
+ m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::SILENCEMODE, &silence, sizeof(bool));
+
+ delete m_encoder;
+ m_encoder = NULL;
+
+ if (m_encoderBuffers)
+ {
+ m_discardBufferPools.push_back(m_encoderBuffers);
+ m_encoderBuffers = NULL;
+ }
+ if (m_vizBuffers)
+ {
+ m_discardBufferPools.push_back(m_vizBuffers);
+ m_vizBuffers = NULL;
+ }
+ }
+ // resample buffers for streams
+ else
+ {
+ bool silence = true;
+ m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::SILENCEMODE, &silence, sizeof(bool));
+
+ AEAudioFormat outputFormat;
+ if (m_mode == MODE_RAW)
+ {
+ outputFormat = inputFormat;
+ sinkInputFormat = m_sinkFormat;
+ }
+ // transcode everything with more than 2 channels
+ else if (m_mode == MODE_TRANSCODE)
+ {
+ outputFormat = inputFormat;
+ outputFormat.m_dataFormat = AE_FMT_FLOATP;
+
+ if (g_advancedSettings.m_audioResample)
+ {
+ outputFormat.m_sampleRate = g_advancedSettings.m_audioResample;
+ CLog::Log(LOGINFO, "CActiveAE::Configure - Forcing samplerate to %d", inputFormat.m_sampleRate);
+ }
+
+ // setup encoder
+ if (!m_encoder)
+ {
+ m_encoder = new CAEEncoderFFmpeg();
+ m_encoder->Initialize(outputFormat, true);
+ m_encoderFormat = outputFormat;
+ }
+ else
+ outputFormat = m_encoderFormat;
+
+ outputFormat.m_channelLayout = m_encoderFormat.m_channelLayout;
+ outputFormat.m_frames = m_encoderFormat.m_frames;
+
+ // encoder buffer
+ if (m_encoder->GetCodecID() == CODEC_ID_AC3)
+ {
+ AEAudioFormat format;
+ format.m_channelLayout = AE_CH_LAYOUT_2_0;
+ format.m_dataFormat = AE_FMT_S16NE;
+ format.m_frameSize = 2* (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+ format.m_frames = AC3_FRAME_SIZE;
+ format.m_sampleRate = 48000;
+ if (m_encoderBuffers && initSink)
+ {
+ m_discardBufferPools.push_back(m_encoderBuffers);
+ m_encoderBuffers = NULL;
+ }
+ if (!m_encoderBuffers)
+ {
+ m_encoderBuffers = new CActiveAEBufferPool(format);
+ m_encoderBuffers->Create(MAX_WATER_LEVEL*1000);
+ }
+ }
+
+ sinkInputFormat = m_sinkFormat;
+ }
+ else
+ {
+ outputFormat = m_sinkFormat;
+ outputFormat.m_channelLayout = m_sinkRequestFormat.m_channelLayout;
+ outputFormat.m_channelLayout.ResolveChannels(m_sinkFormat.m_channelLayout);
+ outputFormat.m_dataFormat = AE_FMT_FLOAT;
+ outputFormat.m_frameSize = outputFormat.m_channelLayout.Count() *
+ (CAEUtil::DataFormatToBits(outputFormat.m_dataFormat) >> 3);
+ // TODO: adjust to decoder
+ sinkInputFormat = outputFormat;
+ }
+ m_internalFormat = outputFormat;
+
+ std::list<CActiveAEStream*>::iterator it;
+ for(it=m_streams.begin(); it!=m_streams.end(); ++it)
+ {
+ // check if we support input format of stream
+ if (!AE_IS_RAW((*it)->m_format.m_dataFormat) &&
+ CActiveAEResample::GetAVSampleFormat((*it)->m_format.m_dataFormat) == AV_SAMPLE_FMT_FLT &&
+ (*it)->m_format.m_dataFormat != AE_FMT_FLOAT)
+ {
+ (*it)->m_convertFn = CAEConvert::ToFloat((*it)->m_format.m_dataFormat);
+ (*it)->m_format.m_dataFormat = AE_FMT_FLOAT;
+ }
+
+ if (!(*it)->m_inputBuffers)
+ {
+ // align input buffers with period of sink or encoder
+ (*it)->m_format.m_frames = m_internalFormat.m_frames * ((float)(*it)->m_format.m_sampleRate / m_internalFormat.m_sampleRate);
+
+ // create buffer pool
+ (*it)->m_inputBuffers = new CActiveAEBufferPool((*it)->m_format);
+ (*it)->m_inputBuffers->Create(MAX_CACHE_LEVEL*1000);
+ }
+ if (initSink && (*it)->m_resampleBuffers)
+ {
+ m_discardBufferPools.push_back((*it)->m_resampleBuffers);
+ (*it)->m_resampleBuffers = NULL;
+ }
+ if (!(*it)->m_resampleBuffers)
+ {
+ (*it)->m_resampleBuffers = new CActiveAEBufferPoolResample((*it)->m_inputBuffers->m_format, outputFormat, m_settings.resampleQuality);
+ (*it)->m_resampleBuffers->Create(MAX_CACHE_LEVEL*1000, false);
+ }
+ if (m_mode == MODE_TRANSCODE || m_streams.size() > 1)
+ (*it)->m_resampleBuffers->m_fillPackets = true;
+ }
+
+ // buffers for viz
+ if (!AE_IS_RAW(inputFormat.m_dataFormat))
+ {
+ if (initSink && m_vizBuffers)
+ {
+ m_discardBufferPools.push_back(m_vizBuffers);
+ m_vizBuffers = NULL;
+ }
+ if (!m_vizBuffers)
+ {
+ AEAudioFormat vizFormat = m_internalFormat;
+ vizFormat.m_channelLayout = AE_CH_LAYOUT_2_0;
+ vizFormat.m_dataFormat = AE_FMT_FLOAT;
+ m_vizBuffers = new CActiveAEBufferPoolResample(m_internalFormat, vizFormat, m_settings.resampleQuality);
+ // TODO use cache of sync + water level
+ m_vizBuffers->Create(2000, false);
+ m_vizInitialized = false;
+ }
+ }
+ }
+
+ // resample buffers for sink
+ if (m_sinkBuffers && !m_sink.IsCompatible(m_sinkBuffers->m_format, device))
+ {
+ m_discardBufferPools.push_back(m_sinkBuffers);
+ m_sinkBuffers = NULL;
+ }
+ if (!m_sinkBuffers)
+ {
+ m_sinkBuffers = new CActiveAEBufferPoolResample(sinkInputFormat, m_sinkFormat, m_settings.resampleQuality);
+ m_sinkBuffers->Create(MAX_WATER_LEVEL*1000, true);
+ }
+
+ // reset gui sounds
+ std::vector<CActiveAESound*>::iterator it;
+ for (it = m_sounds.begin(); it != m_sounds.end(); ++it)
+ {
+ (*it)->SetConverted(false);
+ }
+
+ ClearDiscardedBuffers();
+ m_extDrain = false;
+}
+
+CActiveAEStream* CActiveAE::CreateStream(MsgStreamNew *streamMsg)
+{
+ // we only can handle a single pass through stream
+ if (!m_streams.empty())
+ {
+ if (AE_IS_RAW(m_streams.front()->m_format.m_dataFormat) || AE_IS_RAW(streamMsg->format.m_dataFormat))
+ return NULL;
+ }
+
+ // create the stream
+ CActiveAEStream *stream;
+ stream = new CActiveAEStream(&streamMsg->format);
+ stream->m_streamPort = new CActiveAEDataProtocol("stream",
+ &stream->m_inMsgEvent, &m_outMsgEvent);
+
+ // create buffer pool
+ stream->m_inputBuffers = NULL; // create in Configure when we know the sink format
+ stream->m_resampleBuffers = NULL; // create in Configure when we know the sink format
+ stream->m_statsLock = m_stats.GetLock();
+ stream->m_fadingSamples = 0;
+ stream->m_started = false;
+
+ if (streamMsg->options & AESTREAM_PAUSED)
+ stream->m_paused = true;
+
+ m_streams.push_back(stream);
+
+ return stream;
+}
+
+void CActiveAE::DiscardStream(CActiveAEStream *stream)
+{
+ std::list<CActiveAEStream*>::iterator it;
+ for (it=m_streams.begin(); it!=m_streams.end(); )
+ {
+ if (stream == (*it))
+ {
+ while (!(*it)->m_processingSamples.empty())
+ {
+ (*it)->m_processingSamples.front()->Return();
+ (*it)->m_processingSamples.pop_front();
+ }
+ m_discardBufferPools.push_back((*it)->m_inputBuffers);
+ m_discardBufferPools.push_back((*it)->m_resampleBuffers);
+ CLog::Log(LOGDEBUG, "CActiveAE::DiscardStream - audio stream deleted");
+ delete (*it)->m_streamPort;
+ delete (*it);
+ it = m_streams.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ ClearDiscardedBuffers();
+}
+
+void CActiveAE::SFlushStream(CActiveAEStream *stream)
+{
+ while (!stream->m_processingSamples.empty())
+ {
+ stream->m_processingSamples.front()->Return();
+ stream->m_processingSamples.pop_front();
+ }
+ stream->m_resampleBuffers->Flush();
+ stream->m_streamPort->Purge();
+ stream->m_bufferedTime = 0.0;
+ stream->m_paused = true;
+}
+
+void CActiveAE::ClearDiscardedBuffers()
+{
+ std::list<CActiveAEBufferPool*>::iterator it;
+ for (it=m_discardBufferPools.begin(); it!=m_discardBufferPools.end(); ++it)
+ {
+ CActiveAEBufferPoolResample *rbuf = dynamic_cast<CActiveAEBufferPoolResample*>(*it);
+ if (rbuf)
+ {
+ rbuf->Flush();
+ }
+ // if all buffers have returned, we can delete the buffer pool
+ if ((*it)->m_allSamples.size() == (*it)->m_freeSamples.size())
+ {
+ delete (*it);
+ CLog::Log(LOGDEBUG, "CActiveAE::ClearDiscardedBuffers - buffer pool deleted");
+ m_discardBufferPools.erase(it);
+ return;
+ }
+ }
+}
+
+void CActiveAE::SStopSound(CActiveAESound *sound)
+{
+ std::list<SoundState>::iterator it;
+ for (it=m_sounds_playing.begin(); it!=m_sounds_playing.end(); ++it)
+ {
+ if (it->sound == sound)
+ {
+ m_sounds_playing.erase(it);
+ return;
+ }
+ }
+}
+
+void CActiveAE::DiscardSound(CActiveAESound *sound)
+{
+ SStopSound(sound);
+
+ std::vector<CActiveAESound*>::iterator it;
+ for (it=m_sounds.begin(); it!=m_sounds.end(); ++it)
+ {
+ if ((*it) == sound)
+ {
+ m_sounds.erase(it);
+ return;
+ }
+ }
+}
+
+float CActiveAE::CalcStreamAmplification(CActiveAEStream *stream, CSampleBuffer *buf)
+{
+ float amp = 1.0f;
+ int nb_floats = buf->pkt->nb_samples * buf->pkt->config.channels / buf->pkt->planes;
+ float tamp;
+ for(int i=0; i<buf->pkt->planes; i++)
+ {
+ tamp = stream->m_limiter.Run((float*)buf->pkt->data[i], nb_floats);
+ amp = std::min(amp, tamp);
+ }
+ return amp;
+}
+
+void CActiveAE::ChangeResampleQuality()
+{
+ std::list<CActiveAEStream*>::iterator it;
+ for(it=m_streams.begin(); it!=m_streams.end(); ++it)
+ {
+ if ((*it)->m_resampleBuffers && (*it)->m_resampleBuffers->m_resampler && ((*it)->m_resampleBuffers->m_resampleQuality != m_settings.resampleQuality))
+ (*it)->m_resampleBuffers->m_changeResampler = true;
+ (*it)->m_resampleBuffers->m_resampleQuality = m_settings.resampleQuality;
+ }
+}
+
+void CActiveAE::ApplySettingsToFormat(AEAudioFormat &format, AudioSettings &settings, bool setmode)
+{
+ // raw pass through
+ if (m_settings.mode != AUDIO_ANALOG && AE_IS_RAW(format.m_dataFormat))
+ {
+ if ((format.m_dataFormat == AE_FMT_AC3 && !settings.ac3passthrough) ||
+ (format.m_dataFormat == AE_FMT_TRUEHD && !settings.truehdpassthrough) ||
+ (format.m_dataFormat == AE_FMT_DTS && !settings.dtspassthrough) ||
+ (format.m_dataFormat == AE_FMT_DTSHD && !settings.dtshdpassthrough))
+ {
+ CLog::Log(LOGERROR, "CActiveAE::ApplySettingsToFormat - input audio format is wrong");
+ }
+ if (setmode)
+ m_mode = MODE_RAW;
+ }
+ // transcode
+ else if (m_settings.mode != AUDIO_ANALOG &&
+ settings.ac3passthrough &&
+ (!settings.multichannellpcm || (m_settings.mode != AUDIO_HDMI)) &&
+ !m_streams.empty() &&
+ format.m_channelLayout.Count() > 2)
+ {
+ format.m_dataFormat = AE_FMT_AC3;
+ format.m_sampleRate = 48000;
+ if (setmode)
+ m_mode = MODE_TRANSCODE;
+ }
+ else
+ {
+ format.m_dataFormat = AE_FMT_FLOAT;
+ if ((format.m_channelLayout.Count() > 2) || settings.stereoupmix)
+ {
+ switch (settings.channels)
+ {
+ default:
+ case 0: format.m_channelLayout = AE_CH_LAYOUT_2_0; break;
+ case 1: format.m_channelLayout = AE_CH_LAYOUT_2_0; break;
+ case 2: format.m_channelLayout = AE_CH_LAYOUT_2_1; break;
+ case 3: format.m_channelLayout = AE_CH_LAYOUT_3_0; break;
+ case 4: format.m_channelLayout = AE_CH_LAYOUT_3_1; break;
+ case 5: format.m_channelLayout = AE_CH_LAYOUT_4_0; break;
+ case 6: format.m_channelLayout = AE_CH_LAYOUT_4_1; break;
+ case 7: format.m_channelLayout = AE_CH_LAYOUT_5_0; break;
+ case 8: format.m_channelLayout = AE_CH_LAYOUT_5_1; break;
+ case 9: format.m_channelLayout = AE_CH_LAYOUT_7_0; break;
+ case 10: format.m_channelLayout = AE_CH_LAYOUT_7_1; break;
+ }
+ }
+
+ if (g_advancedSettings.m_audioResample)
+ {
+ format.m_sampleRate = g_advancedSettings.m_audioResample;
+ CLog::Log(LOGINFO, "CActiveAE::ApplySettings - Forcing samplerate to %d", format.m_sampleRate);
+ }
+
+ // for IEC958 limit to 2 channels
+ if (m_settings.mode == AUDIO_IEC958)
+ {
+ format.m_channelLayout = AE_CH_LAYOUT_2_0;
+ }
+
+ CAEChannelInfo stdLayout = format.m_channelLayout;
+ format.m_channelLayout.ResolveChannels(stdLayout);
+ }
+}
+
+bool CActiveAE::NeedReconfigureBuffers()
+{
+ AEAudioFormat newFormat = m_sinkRequestFormat;
+ ApplySettingsToFormat(newFormat, m_settings);
+
+ if (newFormat.m_dataFormat != m_sinkRequestFormat.m_dataFormat ||
+ newFormat.m_channelLayout != m_sinkRequestFormat.m_channelLayout ||
+ newFormat.m_sampleRate != m_sinkRequestFormat.m_sampleRate)
+ return true;
+
+ return false;
+}
+
+bool CActiveAE::NeedReconfigureSink()
+{
+ AEAudioFormat newFormat = m_sinkRequestFormat;
+ ApplySettingsToFormat(newFormat, m_settings);
+
+ std::string device = AE_IS_RAW(newFormat.m_dataFormat) ? m_settings.passthoughdevice : m_settings.device;
+ std::string driver;
+ CAESinkFactory::ParseDevice(device, driver);
+ if (m_settings.driver.compare(driver) != 0)
+ return true;
+
+ if (!m_sink.IsCompatible(newFormat, device))
+ return true;
+
+ return false;
+}
+
+bool CActiveAE::InitSink()
+{
+ SinkConfig config;
+ config.format = m_sinkRequestFormat;
+ config.stats = &m_stats;
+
+ // send message to sink
+ Message *reply;
+ if (m_sink.m_controlPort.SendOutMessageSync(CSinkControlProtocol::CONFIGURE,
+ &reply,
+ 5000,
+ &config, sizeof(config)))
+ {
+ bool success = reply->signal == CSinkControlProtocol::ACC ? true : false;
+ if (!success)
+ {
+ reply->Release();
+ CLog::Log(LOGERROR, "ActiveAE::%s - returned error", __FUNCTION__);
+ m_extError = true;
+ return false;
+ }
+ AEAudioFormat *data;
+ data = (AEAudioFormat*)reply->data;
+ if (data)
+ {
+ m_sinkFormat = *data;
+ }
+ m_sinkHasVolume = m_sink.HasVolume();
+ reply->Release();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - failed to init", __FUNCTION__);
+ m_extError = true;
+ return false;
+ }
+
+ m_inMsgEvent.Reset();
+ return true;
+}
+
+void CActiveAE::DrainSink()
+{
+ // send message to sink
+ Message *reply;
+ if (m_sink.m_dataPort.SendOutMessageSync(CSinkDataProtocol::DRAIN,
+ &reply,
+ 2000))
+ {
+ bool success = reply->signal == CSinkDataProtocol::ACC ? true : false;
+ if (!success)
+ {
+ reply->Release();
+ CLog::Log(LOGERROR, "ActiveAE::%s - returned error on drain", __FUNCTION__);
+ m_extError = true;
+ return;
+ }
+ reply->Release();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - failed to drain", __FUNCTION__);
+ m_extError = true;
+ return;
+ }
+}
+
+void CActiveAE::UnconfigureSink()
+{
+ // send message to sink
+ Message *reply;
+ if (m_sink.m_controlPort.SendOutMessageSync(CSinkControlProtocol::UNCONFIGURE,
+ &reply,
+ 2000))
+ {
+ bool success = reply->signal == CSinkControlProtocol::ACC ? true : false;
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - returned error", __FUNCTION__);
+ m_extError = true;
+ }
+ reply->Release();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - failed to unconfigure", __FUNCTION__);
+ m_extError = true;
+ }
+
+ m_inMsgEvent.Reset();
+}
+
+
+bool CActiveAE::RunStages()
+{
+ bool busy = false;
+
+ // serve input streams
+ std::list<CActiveAEStream*>::iterator it;
+ for (it = m_streams.begin(); it != m_streams.end(); ++it)
+ {
+ if ((*it)->m_resampleBuffers && !(*it)->m_paused)
+ busy = (*it)->m_resampleBuffers->ResampleBuffers();
+ else if ((*it)->m_resampleBuffers &&
+ ((*it)->m_resampleBuffers->m_inputSamples.size() > (*it)->m_resampleBuffers->m_allSamples.size() * 0.5))
+ {
+ CSingleLock lock((*it)->m_streamLock);
+ (*it)->m_streamIsBuffering = false;
+ }
+
+ // provide buffers to stream
+ float time = m_stats.GetCacheTime((*it));
+ CSampleBuffer *buffer;
+ if (!(*it)->m_drain)
+ {
+ while (time < MAX_CACHE_LEVEL && !(*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;
+ }
+ }
+ else
+ {
+ if ((*it)->m_inputBuffers->m_allSamples.size() == (*it)->m_inputBuffers->m_freeSamples.size())
+ {
+ (*it)->m_streamPort->SendInMessage(CActiveAEDataProtocol::STREAMDRAINED);
+ (*it)->m_drain = false;
+ (*it)->m_resampleBuffers->m_drain = false;
+ (*it)->m_started = false;
+
+ // set variables being polled via stream interface
+ CSingleLock lock((*it)->m_streamLock);
+ if ((*it)->m_streamSlave)
+ {
+ CActiveAEStream *slave = (CActiveAEStream*)((*it)->m_streamSlave);
+ slave->m_paused = false;
+ Configure(&slave->m_format);
+ (*it)->m_streamSlave = NULL;
+ }
+ (*it)->m_streamDrained = true;
+ (*it)->m_streamDraining = false;
+ }
+ }
+ }
+
+ if (m_stats.GetWaterLevel() < MAX_WATER_LEVEL &&
+ (m_mode != MODE_TRANSCODE || (m_encoderBuffers && !m_encoderBuffers->m_freeSamples.empty())))
+ {
+ // mix streams and sounds sounds
+ if (m_mode != MODE_RAW)
+ {
+ CSampleBuffer *out = NULL;
+ if (!m_sounds_playing.empty() && m_streams.empty())
+ {
+ if (m_silenceBuffers && !m_silenceBuffers->m_freeSamples.empty())
+ {
+ out = m_silenceBuffers->GetFreeBuffer();
+ for (int i=0; i<out->pkt->planes; i++)
+ {
+ memset(out->pkt->data[i], 0, out->pkt->linesize);
+ }
+ out->pkt->nb_samples = out->pkt->max_nb_samples;
+ }
+ }
+
+ // mix streams
+ std::list<CActiveAEStream*>::iterator it;
+
+ // if we deal with more than a single stream, all streams
+ // must provide samples for mixing
+ bool allStreamsReady = true;
+ for (it = m_streams.begin(); it != m_streams.end(); ++it)
+ {
+ if ((*it)->m_paused || !(*it)->m_started || !(*it)->m_resampleBuffers)
+ continue;
+
+ if ((*it)->m_resampleBuffers->m_outputSamples.empty())
+ allStreamsReady = false;
+ }
+
+ for (it = m_streams.begin(); it != m_streams.end() && allStreamsReady; ++it)
+ {
+ if ((*it)->m_paused || !(*it)->m_resampleBuffers)
+ continue;
+
+ if (!(*it)->m_resampleBuffers->m_outputSamples.empty())
+ {
+ (*it)->m_started = true;
+
+ if (!out)
+ {
+ out = (*it)->m_resampleBuffers->m_outputSamples.front();
+ (*it)->m_resampleBuffers->m_outputSamples.pop_front();
+
+ // volume for stream
+ float amp = (*it)->m_rgain * CalcStreamAmplification((*it), out);
+
+ int nb_floats = out->pkt->nb_samples * out->pkt->config.channels / out->pkt->planes;
+ int nb_loops = 1;
+ float fadingStep;
+
+ // fading
+ 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)
+ {
+ nb_floats = out->pkt->config.channels / out->pkt->planes;
+ nb_loops = out->pkt->nb_samples;
+ float delta = (*it)->m_fadingTarget - (*it)->m_fadingBase;
+ int samples = m_internalFormat.m_sampleRate * (float)(*it)->m_fadingTime / 1000.0f;
+ fadingStep = delta / samples;
+ }
+ for(int i=0; i<nb_loops; i++)
+ {
+ if ((*it)->m_fadingSamples > 0)
+ {
+ (*it)->m_volume += fadingStep;
+ (*it)->m_fadingSamples--;
+
+ if ((*it)->m_fadingSamples == 0)
+ {
+ // set variables being polled via stream interface
+ CSingleLock lock((*it)->m_streamLock);
+ (*it)->m_streamFading = false;
+ }
+ }
+ float volume = (*it)->m_volume * amp;
+
+ for(int j=0; j<out->pkt->planes; j++)
+ {
+#ifdef __SSE__
+ CAEUtil::SSEMulArray((float*)out->pkt->data[j]+i*nb_floats, m_muted ? 0.0 : volume, nb_floats);
+#else
+ float* fbuffer = (float*) out->pkt->data[j]+i*nb_floats;
+ for (int k = 0; k < nb_floats; ++k)
+ *fbuffer++ *= m_muted ? 0.0 : volume;
+#endif
+ }
+ }
+ }
+ else
+ {
+ CSampleBuffer *mix = NULL;
+ mix = (*it)->m_resampleBuffers->m_outputSamples.front();
+ (*it)->m_resampleBuffers->m_outputSamples.pop_front();
+
+ // volume for stream
+ float amp = (*it)->m_volume * (*it)->m_rgain * CalcStreamAmplification((*it), mix);
+
+ int nb_floats = mix->pkt->nb_samples * mix->pkt->config.channels / mix->pkt->planes;
+ int nb_loops = 1;
+ float fadingStep;
+
+ // fading
+ 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)
+ {
+ nb_floats = mix->pkt->config.channels / mix->pkt->planes;
+ nb_loops = mix->pkt->nb_samples;
+ float delta = (*it)->m_fadingTarget - (*it)->m_fadingBase;
+ int samples = m_internalFormat.m_sampleRate * (float)(*it)->m_fadingTime / 1000.0f;
+ fadingStep = delta / samples;
+ }
+ for(int i=0; i<nb_loops; i++)
+ {
+ if ((*it)->m_fadingSamples > 0)
+ {
+ (*it)->m_volume += fadingStep;
+ (*it)->m_fadingSamples--;
+
+ if ((*it)->m_fadingSamples == 0)
+ {
+ // set variables being polled via stream interface
+ CSingleLock lock((*it)->m_streamLock);
+ (*it)->m_streamFading = false;
+ }
+ }
+ float volume = (*it)->m_volume * amp;
+
+ for(int j=0; j<out->pkt->planes && j<mix->pkt->planes; j++)
+ {
+ float *dst = (float*)out->pkt->data[j]+i*nb_floats;
+ float *src = (float*)mix->pkt->data[j]+i*nb_floats;
+#ifdef __SSE__
+ CAEUtil::SSEMulAddArray(dst, src, m_muted ? 0.0 : volume, nb_floats);
+#else
+ for (int k = 0; k < nb_floats; ++k)
+ *dst++ += *src++ * m_muted ? 0.0 : volume;
+#endif
+ }
+ }
+ mix->Return();
+ }
+ busy = true;
+ }
+ }
+
+ // process output buffer, gui sounds, encode, viz
+ if (out)
+ {
+ // mix gui sounds
+ MixSounds(*(out->pkt));
+ if (!m_sinkHasVolume)
+ Deamplify(*(out->pkt));
+
+ // encode
+ if (m_mode == MODE_TRANSCODE && m_encoder)
+ {
+ CSampleBuffer *buf = m_encoderBuffers->GetFreeBuffer();
+ int ret = m_encoder->Encode(out->pkt->data[0], out->pkt->planes*out->pkt->linesize,
+ buf->pkt->data[0], buf->pkt->planes*buf->pkt->linesize);
+ buf->pkt->nb_samples = buf->pkt->max_nb_samples;
+ out->Return();
+ out = buf;
+ }
+
+ // update stats
+ m_stats.AddSamples(out->pkt->nb_samples, m_streams);
+ m_sinkBuffers->m_inputSamples.push_back(out);
+
+ busy = true;
+ }
+
+ // viz
+ {
+ CSingleLock lock(m_vizLock);
+ if (m_audioCallback && m_vizBuffers && !m_streams.empty())
+ {
+ if (!m_vizInitialized)
+ {
+ m_audioCallback->OnInitialize(2, m_vizBuffers->m_format.m_sampleRate, 32);
+ m_vizInitialized = true;
+ }
+
+ // if viz has no free buffer, it won't return current buffer "out"
+ if (!m_vizBuffers->m_freeSamples.empty())
+ {
+ if (out)
+ {
+ out->Acquire();
+ m_vizBuffers->m_inputSamples.push_back(out);
+ }
+ }
+ else
+ CLog::Log(LOGWARNING,"ActiveAE::%s - viz ran out of free buffers", __FUNCTION__);
+ unsigned int now = XbmcThreads::SystemClockMillis();
+ unsigned int timestamp = now + m_stats.GetDelay() * 1000;
+ busy |= m_vizBuffers->ResampleBuffers(timestamp);
+ while(!m_vizBuffers->m_outputSamples.empty())
+ {
+ CSampleBuffer *buf = m_vizBuffers->m_outputSamples.front();
+ if ((now - buf->timestamp) & 0x80000000)
+ break;
+ else
+ {
+ int submitted = 0;
+ int samples;
+ while(submitted < buf->pkt->nb_samples)
+ {
+ samples = std::min(512, buf->pkt->nb_samples-submitted);
+ m_audioCallback->OnAudioData((float*)(buf->pkt->data[0]+2*submitted), samples);
+ submitted += samples;
+ }
+ buf->Return();
+ m_vizBuffers->m_outputSamples.pop_front();
+ }
+ }
+ }
+ else if (m_vizBuffers)
+ m_vizBuffers->Flush();
+ }
+ }
+ // pass through
+ else
+ {
+ std::list<CActiveAEStream*>::iterator it;
+ CSampleBuffer *buffer;
+ for (it = m_streams.begin(); it != m_streams.end(); ++it)
+ {
+ if (!(*it)->m_resampleBuffers->m_outputSamples.empty())
+ {
+ buffer = (*it)->m_resampleBuffers->m_outputSamples.front();
+ (*it)->m_resampleBuffers->m_outputSamples.pop_front();
+ m_stats.AddSamples(buffer->pkt->nb_samples, m_streams);
+ m_sinkBuffers->m_inputSamples.push_back(buffer);
+ }
+ }
+ }
+
+ // 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;
+}
+
+bool CActiveAE::HasWork()
+{
+ if (!m_sounds_playing.empty())
+ return true;
+ if (!m_sinkBuffers->m_inputSamples.empty())
+ return true;
+ if (!m_sinkBuffers->m_outputSamples.empty())
+ return true;
+
+ std::list<CActiveAEStream*>::iterator it;
+ for (it = m_streams.begin(); it != m_streams.end(); ++it)
+ {
+ if (!(*it)->m_resampleBuffers->m_inputSamples.empty())
+ return true;
+ if (!(*it)->m_resampleBuffers->m_outputSamples.empty())
+ return true;
+ if (!(*it)->m_processingSamples.empty())
+ return true;
+ }
+
+ return false;
+}
+
+void CActiveAE::MixSounds(CSoundPacket &dstSample)
+{
+ if (m_sounds_playing.empty())
+ return;
+
+ float volume;
+ float *out;
+ float *sample_buffer;
+ int max_samples = dstSample.nb_samples;
+
+ std::list<SoundState>::iterator it;
+ for (it = m_sounds_playing.begin(); it != m_sounds_playing.end(); )
+ {
+ if (!it->sound->IsConverted())
+ ResampleSound(it->sound);
+ int available_samples = it->sound->GetSound(false)->nb_samples - it->samples_played;
+ int mix_samples = std::min(max_samples, available_samples);
+ int start = it->samples_played *
+ m_dllAvUtil.av_get_bytes_per_sample(it->sound->GetSound(false)->config.fmt) *
+ it->sound->GetSound(false)->config.channels /
+ it->sound->GetSound(false)->planes;
+
+ for(int j=0; j<dstSample.planes; j++)
+ {
+ volume = it->sound->GetVolume();
+ out = (float*)dstSample.data[j];
+ sample_buffer = (float*)(it->sound->GetSound(false)->data[j]+start);
+ int nb_floats = mix_samples * dstSample.config.channels / dstSample.planes;
+#ifdef __SSE__
+ CAEUtil::SSEMulAddArray(out, sample_buffer, volume, nb_floats);
+#else
+ for (int k = 0; k < nb_floats; ++k)
+ *out++ += *sample_buffer++ * volume;
+#endif
+ }
+
+ it->samples_played += mix_samples;
+
+ // no more frames, so remove it from the list
+ if (it->samples_played >= it->sound->GetSound(false)->nb_samples)
+ {
+ it = m_sounds_playing.erase(it);
+ continue;
+ }
+ ++it;
+ }
+}
+
+void CActiveAE::Deamplify(CSoundPacket &dstSample)
+{
+ if (m_volume < 1.0)
+ {
+ float *buffer;
+ int nb_floats = dstSample.nb_samples * dstSample.config.channels / dstSample.planes;
+
+ 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);
+#else
+ float *fbuffer = buffer;
+ for (unsigned int i = 0; i < nb_floats; i++)
+ *fbuffer++ *= m_volume;
+#endif
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Configuration
+//-----------------------------------------------------------------------------
+
+void CActiveAE::LoadSettings()
+{
+ m_settings.device = CSettings::Get().GetString("audiooutput.audiodevice");
+ m_settings.passthoughdevice = CSettings::Get().GetString("audiooutput.passthroughdevice");
+
+ m_settings.mode = CSettings::Get().GetInt("audiooutput.mode");
+ m_settings.channels = CSettings::Get().GetInt("audiooutput.channels");
+
+ m_settings.stereoupmix = CSettings::Get().GetBool("audiooutput.stereoupmix");
+ m_settings.ac3passthrough = CSettings::Get().GetBool("audiooutput.ac3passthrough");
+ m_settings.truehdpassthrough = CSettings::Get().GetBool("audiooutput.truehdpassthrough");
+ m_settings.dtspassthrough = CSettings::Get().GetBool("audiooutput.dtspassthrough");
+ m_settings.dtshdpassthrough = CSettings::Get().GetBool("audiooutput.dtshdpassthrough");
+ m_settings.aacpassthrough = CSettings::Get().GetBool("audiooutput.passthroughaac");
+ m_settings.multichannellpcm = CSettings::Get().GetBool("audiooutput.multichannellpcm");
+
+ m_settings.resampleQuality = static_cast<AEQuality>(CSettings::Get().GetInt("audiooutput.processquality"));
+}
+
+bool CActiveAE::Initialize()
+{
+ if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load())
+ {
+ CLog::Log(LOGERROR,"CActiveAE::Initialize - failed to load ffmpeg libraries");
+ return false;
+ }
+ m_dllAvFormat.av_register_all();
+
+ Create();
+ Message *reply;
+ if (m_controlPort.SendOutMessageSync(CActiveAEControlProtocol::INIT,
+ &reply,
+ 5000))
+ {
+ bool success = reply->signal == CActiveAEControlProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - returned error", __FUNCTION__);
+ Dispose();
+ return false;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - failed to init", __FUNCTION__);
+ Dispose();
+ return false;
+ }
+
+ // hook into windowing for receiving display reset events
+#if defined(HAS_GLX) || defined(TARGET_DARWIN_OSX)
+ g_Windowing.Register(this);
+#endif
+
+ m_inMsgEvent.Reset();
+ return true;
+}
+
+void CActiveAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
+{
+ m_sink.EnumerateOutputDevices(devices, passthrough);
+}
+
+std::string CActiveAE::GetDefaultDevice(bool passthrough)
+{
+ return m_sink.GetDefaultDevice(passthrough);
+}
+
+void CActiveAE::OnSettingsChange(const std::string& setting)
+{
+ if (setting == "audiooutput.passthroughdevice" ||
+ setting == "audiooutput.audiodevice" ||
+ setting == "audiooutput.mode" ||
+ setting == "audiooutput.ac3passthrough" ||
+ setting == "audiooutput.dtspassthrough" ||
+ setting == "audiooutput.passthroughaac" ||
+ setting == "audiooutput.truehdpassthrough" ||
+ setting == "audiooutput.dtshdpassthrough" ||
+ setting == "audiooutput.channels" ||
+ setting == "audiooutput.multichannellpcm" ||
+ setting == "audiooutput.stereoupmix" ||
+ setting == "audiooutput.streamsilence" ||
+ setting == "audiooutput.processquality")
+ {
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::RECONFIGURE);
+ }
+}
+
+bool CActiveAE::SupportsRaw()
+{
+ return true;
+}
+
+bool CActiveAE::SupportsDrain()
+{
+ return true;
+}
+
+bool CActiveAE::SupportsQualityLevel(enum AEQuality level)
+{
+ if (level == AE_QUALITY_LOW || level == AE_QUALITY_MID || level == AE_QUALITY_HIGH)
+ return true;
+
+ return false;
+}
+
+void CActiveAE::Shutdown()
+{
+ Dispose();
+}
+
+bool CActiveAE::Suspend()
+{
+ return m_controlPort.SendOutMessage(CActiveAEControlProtocol::SUSPEND);
+}
+
+bool CActiveAE::Resume()
+{
+ Message *reply;
+ if (m_controlPort.SendOutMessageSync(CActiveAEControlProtocol::INIT,
+ &reply,
+ 5000))
+ {
+ bool success = reply->signal == CActiveAEControlProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - returned error", __FUNCTION__);
+ return false;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "ActiveAE::%s - failed to init", __FUNCTION__);
+ return false;
+ }
+
+ m_inMsgEvent.Reset();
+ return true;
+}
+
+bool CActiveAE::IsSuspended()
+{
+ return m_stats.IsSuspended();
+}
+
+float CActiveAE::GetVolume()
+{
+ return m_aeVolume;
+}
+
+void CActiveAE::SetVolume(const float volume)
+{
+ m_aeVolume = std::max( 0.0f, std::min(1.0f, volume));
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::VOLUME, &m_aeVolume, sizeof(float));
+}
+
+void CActiveAE::SetMute(const bool enabled)
+{
+ m_aeMuted = enabled;
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::MUTE, &m_aeMuted, sizeof(bool));
+}
+
+bool CActiveAE::IsMuted()
+{
+ return m_aeMuted;
+}
+
+void CActiveAE::SetSoundMode(const int mode)
+{
+ int soundmode = mode;
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::SOUNDMODE, &soundmode, sizeof(int));
+}
+
+
+void CActiveAE::OnLostDevice()
+{
+// m_controlPort.SendOutMessage(CActiveAEControlProtocol::DISPLAYLOST);
+}
+
+void CActiveAE::OnResetDevice()
+{
+// m_controlPort.SendOutMessage(CActiveAEControlProtocol::DISPLAYRESET);
+}
+
+//-----------------------------------------------------------------------------
+// Utils
+//-----------------------------------------------------------------------------
+
+uint8_t **CActiveAE::AllocSoundSample(SampleConfig &config, int &samples, int &bytes_per_sample, int &planes, int &linesize)
+{
+ uint8_t **buffer;
+ planes = m_dllAvUtil.av_sample_fmt_is_planar(config.fmt) ? config.channels : 1;
+ buffer = new uint8_t*[planes];
+ m_dllAvUtil.av_samples_alloc(buffer, &linesize, config.channels,
+ samples, config.fmt, 0);
+ bytes_per_sample = m_dllAvUtil.av_get_bytes_per_sample(config.fmt);
+ return buffer;
+}
+
+void CActiveAE::FreeSoundSample(uint8_t **data)
+{
+ m_dllAvUtil.av_freep(data);
+ delete [] data;
+}
+
+//-----------------------------------------------------------------------------
+// GUI Sounds
+//-----------------------------------------------------------------------------
+
+/**
+ * load sound from an audio file and store original format
+ * register the sound in ActiveAE
+ * later when the engine is idle it will convert the sound to sink format
+ */
+
+#define SOUNDBUFFER_SIZE 20480
+
+IAESound *CActiveAE::MakeSound(const std::string& file)
+{
+ AVFormatContext *fmt_ctx = NULL;
+ AVCodecContext *dec_ctx = NULL;
+ AVIOContext *io_ctx;
+ AVInputFormat *io_fmt;
+ AVCodec *dec = NULL;
+ int bit_rate;
+ CActiveAESound *sound = NULL;
+ SampleConfig config;
+
+ sound = new CActiveAESound(file);
+ if (!sound->Prepare())
+ return NULL;
+ int fileSize = sound->GetFileSize();
+
+ fmt_ctx = m_dllAvFormat.avformat_alloc_context();
+ unsigned char* buffer = (unsigned char*)m_dllAvUtil.av_malloc(SOUNDBUFFER_SIZE+FF_INPUT_BUFFER_PADDING_SIZE);
+ io_ctx = m_dllAvFormat.avio_alloc_context(buffer, SOUNDBUFFER_SIZE, 0,
+ sound, CActiveAESound::Read, NULL, CActiveAESound::Seek);
+ io_ctx->max_packet_size = sound->GetChunkSize();
+ if(io_ctx->max_packet_size)
+ io_ctx->max_packet_size *= SOUNDBUFFER_SIZE / io_ctx->max_packet_size;
+
+ if(!sound->IsSeekPosible())
+ io_ctx->seekable = 0;
+
+ fmt_ctx->pb = io_ctx;
+
+ m_dllAvFormat.av_probe_input_buffer(io_ctx, &io_fmt, file.c_str(), NULL, 0, 0);
+ if (!io_fmt)
+ {
+ m_dllAvFormat.avformat_close_input(&fmt_ctx);
+ delete sound;
+ return NULL;
+ }
+
+ // find decoder
+ if (m_dllAvFormat.avformat_open_input(&fmt_ctx, file.c_str(), NULL, NULL) == 0)
+ {
+ fmt_ctx->flags |= AVFMT_FLAG_NOPARSE;
+ if (m_dllAvFormat.avformat_find_stream_info(fmt_ctx, NULL) >= 0)
+ {
+ dec_ctx = fmt_ctx->streams[0]->codec;
+ dec = m_dllAvCodec.avcodec_find_decoder(dec_ctx->codec_id);
+ config.sample_rate = dec_ctx->sample_rate;
+ bit_rate = dec_ctx->bit_rate;
+ config.channels = dec_ctx->channels;
+ config.channel_layout = dec_ctx->channel_layout;
+ }
+ }
+ if (dec == NULL)
+ {
+ m_dllAvFormat.avformat_close_input(&fmt_ctx);
+ delete sound;
+ return NULL;
+ }
+
+ dec_ctx = m_dllAvCodec.avcodec_alloc_context3(dec);
+ dec_ctx->sample_rate = config.sample_rate;
+ dec_ctx->channels = config.channels;
+ if (!config.channel_layout)
+ config.channel_layout = m_dllAvUtil.av_get_default_channel_layout(config.channels);
+ dec_ctx->channel_layout = config.channel_layout;
+
+ AVPacket avpkt;
+ AVFrame *decoded_frame = NULL;
+ decoded_frame = m_dllAvCodec.avcodec_alloc_frame();
+
+ if (m_dllAvCodec.avcodec_open2(dec_ctx, dec, NULL) >= 0)
+ {
+ bool init = false;
+
+ // decode until eof
+ m_dllAvCodec.av_init_packet(&avpkt);
+ int len;
+ while (m_dllAvFormat.av_read_frame(fmt_ctx, &avpkt) >= 0)
+ {
+ int got_frame = 0;
+ len = m_dllAvCodec.avcodec_decode_audio4(dec_ctx, decoded_frame, &got_frame, &avpkt);
+ if (len < 0)
+ {
+ m_dllAvCodec.avcodec_close(dec_ctx);
+ m_dllAvUtil.av_free(dec_ctx);
+ m_dllAvUtil.av_free(&decoded_frame);
+ m_dllAvFormat.avformat_close_input(&fmt_ctx);
+ delete sound;
+ return NULL;
+ }
+ if (got_frame)
+ {
+ if (!init)
+ {
+ int samples = fileSize / m_dllAvUtil.av_get_bytes_per_sample(dec_ctx->sample_fmt) / config.channels;
+ config.fmt = dec_ctx->sample_fmt;
+ sound->InitSound(true, config, samples);
+ init = true;
+ }
+ sound->StoreSound(true, decoded_frame->extended_data,
+ decoded_frame->nb_samples, decoded_frame->linesize[0]);
+ }
+ }
+ m_dllAvCodec.avcodec_close(dec_ctx);
+ }
+
+ m_dllAvUtil.av_free(dec_ctx);
+ m_dllAvUtil.av_free(decoded_frame);
+ m_dllAvFormat.avformat_close_input(&fmt_ctx);
+
+ sound->Finish();
+
+ // register sound
+ m_dataPort.SendOutMessage(CActiveAEDataProtocol::NEWSOUND, &sound, sizeof(CActiveAESound*));
+
+ return sound;
+}
+
+void CActiveAE::FreeSound(IAESound *sound)
+{
+ m_dataPort.SendOutMessage(CActiveAEDataProtocol::FREESOUND, &sound, sizeof(CActiveAESound*));
+}
+
+void CActiveAE::PlaySound(CActiveAESound *sound)
+{
+ m_dataPort.SendOutMessage(CActiveAEDataProtocol::PLAYSOUND, &sound, sizeof(CActiveAESound*));
+}
+
+void CActiveAE::StopSound(CActiveAESound *sound)
+{
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::STOPSOUND, &sound, sizeof(CActiveAESound*));
+}
+
+/**
+ * resample sounds to destination format for mixing
+ * destination format is either format of stream or
+ * default sink format when no stream is playing
+ */
+void CActiveAE::ResampleSounds()
+{
+ std::vector<CActiveAESound*>::iterator it;
+ for (it = m_sounds.begin(); it != m_sounds.end(); ++it)
+ {
+ if (!(*it)->IsConverted())
+ ResampleSound(*it);
+ }
+}
+
+bool CActiveAE::ResampleSound(CActiveAESound *sound)
+{
+ SampleConfig orig_config, dst_config;
+ uint8_t **dst_buffer;
+ int dst_samples;
+
+ if (m_mode == MODE_RAW || m_internalFormat.m_dataFormat == AE_FMT_INVALID)
+ return false;
+
+ if (!sound->GetSound(true))
+ return false;
+
+ orig_config = sound->GetSound(true)->config;
+
+ dst_config.channel_layout = CActiveAEResample::GetAVChannelLayout(m_internalFormat.m_channelLayout);
+ dst_config.channels = m_internalFormat.m_channelLayout.Count();
+ dst_config.sample_rate = m_internalFormat.m_sampleRate;
+ dst_config.fmt = CActiveAEResample::GetAVSampleFormat(m_internalFormat.m_dataFormat);
+
+ CActiveAEResample *resampler = new CActiveAEResample();
+ resampler->Init(dst_config.channel_layout,
+ dst_config.channels,
+ dst_config.sample_rate,
+ dst_config.fmt,
+ orig_config.channel_layout,
+ orig_config.channels,
+ orig_config.sample_rate,
+ orig_config.fmt,
+ NULL,
+ AE_QUALITY_MID);
+
+ dst_samples = resampler->CalcDstSampleCount(sound->GetSound(true)->nb_samples,
+ m_internalFormat.m_sampleRate,
+ orig_config.sample_rate);
+
+ dst_buffer = sound->InitSound(false, dst_config, dst_samples);
+ if (!dst_buffer)
+ {
+ delete resampler;
+ return false;
+ }
+ int samples = resampler->Resample(dst_buffer, dst_samples,
+ sound->GetSound(true)->data,
+ sound->GetSound(true)->nb_samples);
+
+ sound->GetSound(false)->nb_samples = samples;
+
+ delete resampler;
+ sound->SetConverted(true);
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Streams
+//-----------------------------------------------------------------------------
+
+IAEStream *CActiveAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options)
+{
+ //TODO: pass number of samples in audio packet
+
+ AEAudioFormat format;
+ format.m_dataFormat = dataFormat;
+ format.m_sampleRate = sampleRate;
+ format.m_encodedRate = encodedSampleRate;
+ format.m_channelLayout = channelLayout;
+ format.m_frames = format.m_sampleRate / 10;
+ format.m_frameSize = format.m_channelLayout.Count() *
+ (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+
+ MsgStreamNew msg;
+ msg.format = format;
+ msg.options = options;
+
+ Message *reply;
+ if (m_dataPort.SendOutMessageSync(CActiveAEDataProtocol::NEWSTREAM,
+ &reply,1000,
+ &msg, sizeof(MsgStreamNew)))
+ {
+ bool success = reply->signal == CActiveAEControlProtocol::ACC ? true : false;
+ if (success)
+ {
+ CActiveAEStream *stream = *(CActiveAEStream**)reply->data;
+ reply->Release();
+ return stream;
+ }
+ reply->Release();
+ }
+
+ CLog::Log(LOGERROR, "ActiveAE::%s - could not create stream", __FUNCTION__);
+ return NULL;
+}
+
+IAEStream *CActiveAE::FreeStream(IAEStream *stream)
+{
+ m_dataPort.SendOutMessage(CActiveAEDataProtocol::FREESTREAM, &stream, sizeof(IAEStream*));
+ return NULL;
+}
+
+void CActiveAE::FlushStream(CActiveAEStream *stream)
+{
+ Message *reply;
+ if (m_dataPort.SendOutMessageSync(CActiveAEDataProtocol::FLUSHSTREAM,
+ &reply,1000,
+ &stream, sizeof(CActiveAEStream*)))
+ {
+ bool success = reply->signal == CActiveAEDataProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "CActiveAE::FlushStream - failed");
+ }
+ }
+}
+
+void CActiveAE::PauseStream(CActiveAEStream *stream, bool pause)
+{
+ // TODO pause sink, needs api change
+ if (pause)
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::PAUSESTREAM,
+ &stream, sizeof(CActiveAEStream*));
+ else
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::RESUMESTREAM,
+ &stream, sizeof(CActiveAEStream*));
+}
+
+void CActiveAE::SetStreamAmplification(CActiveAEStream *stream, float amplify)
+{
+ MsgStreamParameter msg;
+ msg.stream = stream;
+ msg.parameter.float_par = amplify;
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::STREAMAMP,
+ &msg, sizeof(MsgStreamParameter));
+}
+
+void CActiveAE::SetStreamReplaygain(CActiveAEStream *stream, float rgain)
+{
+ MsgStreamParameter msg;
+ msg.stream = stream;
+ msg.parameter.float_par = rgain;
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::STREAMRGAIN,
+ &msg, sizeof(MsgStreamParameter));
+}
+
+void CActiveAE::SetStreamVolume(CActiveAEStream *stream, float volume)
+{
+ MsgStreamParameter msg;
+ msg.stream = stream;
+ msg.parameter.float_par = volume;
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::STREAMVOLUME,
+ &msg, sizeof(MsgStreamParameter));
+}
+
+void CActiveAE::SetStreamResampleRatio(CActiveAEStream *stream, double ratio)
+{
+ MsgStreamParameter msg;
+ msg.stream = stream;
+ msg.parameter.double_par = ratio;
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::STREAMRESAMPLERATIO,
+ &msg, sizeof(MsgStreamParameter));
+}
+
+void CActiveAE::SetStreamFade(CActiveAEStream *stream, float from, float target, unsigned int millis)
+{
+ MsgStreamFade msg;
+ msg.stream = stream;
+ msg.from = from;
+ msg.target = target;
+ msg.millis = millis;
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::STREAMFADE,
+ &msg, sizeof(MsgStreamFade));
+}
+
+void CActiveAE::RegisterAudioCallback(IAudioCallback* pCallback)
+{
+ CSingleLock lock(m_vizLock);
+ m_audioCallback = pCallback;
+ m_vizInitialized = false;
+}
+
+void CActiveAE::UnregisterAudioCallback()
+{
+ CSingleLock lock(m_vizLock);
+ m_audioCallback = NULL;
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h
new file mode 100644
index 0000000000..90c7f14c7d
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h
@@ -0,0 +1,338 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "threads/Thread.h"
+
+#include "ActiveAESink.h"
+#include "ActiveAEResample.h"
+#include "Interfaces/AEStream.h"
+#include "Interfaces/AESound.h"
+#include "AEFactory.h"
+#include "guilib/DispResource.h"
+
+// ffmpeg
+#include "DllAvFormat.h"
+#include "DllAvCodec.h"
+#include "DllAvUtil.h"
+
+class IAESink;
+class IAEEncoder;
+
+namespace ActiveAE
+{
+
+class CActiveAESound;
+class CActiveAEStream;
+
+struct AudioSettings
+{
+ std::string device;
+ std::string driver;
+ std::string passthoughdevice;
+ int mode;
+ int channels;
+ bool ac3passthrough;
+ bool dtspassthrough;
+ bool aacpassthrough;
+ bool truehdpassthrough;
+ bool dtshdpassthrough;
+ bool multichannellpcm;
+ bool stereoupmix;
+ AEQuality resampleQuality;
+};
+
+class CActiveAEControlProtocol : public Protocol
+{
+public:
+ CActiveAEControlProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {};
+ enum OutSignal
+ {
+ INIT = 0,
+ RECONFIGURE,
+ SUSPEND,
+ MUTE,
+ VOLUME,
+ PAUSESTREAM,
+ RESUMESTREAM,
+ STREAMRGAIN,
+ STREAMVOLUME,
+ STREAMAMP,
+ STREAMRESAMPLERATIO,
+ STREAMFADE,
+ STOPSOUND,
+ SOUNDMODE,
+ GETSTATE,
+ DISPLAYLOST,
+ DISPLAYRESET,
+ TIMEOUT,
+ };
+ enum InSignal
+ {
+ ACC,
+ ERR,
+ STATS,
+ };
+};
+
+class CActiveAEDataProtocol : public Protocol
+{
+public:
+ CActiveAEDataProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {};
+ enum OutSignal
+ {
+ NEWSOUND = 0,
+ PLAYSOUND,
+ FREESOUND,
+ NEWSTREAM,
+ FREESTREAM,
+ STREAMSAMPLE,
+ DRAINSTREAM,
+ FLUSHSTREAM,
+ };
+ enum InSignal
+ {
+ ACC,
+ ERR,
+ STREAMBUFFER,
+ STREAMDRAINED,
+ };
+};
+
+struct MsgStreamNew
+{
+ AEAudioFormat format;
+ unsigned int options;
+};
+
+struct MsgStreamSample
+{
+ CSampleBuffer *buffer;
+ CActiveAEStream *stream;
+};
+
+struct MsgStreamParameter
+{
+ CActiveAEStream *stream;
+ union
+ {
+ float float_par;
+ double double_par;
+ } parameter;
+};
+
+struct MsgStreamFade
+{
+ CActiveAEStream *stream;
+ float from;
+ float target;
+ unsigned int millis;
+};
+
+class CEngineStats
+{
+public:
+ void Reset(unsigned int sampleRate);
+ void UpdateSinkDelay(double delay, int samples);
+ void AddSamples(int samples, std::list<CActiveAEStream*> &streams);
+ float GetDelay();
+ float GetDelay(CActiveAEStream *stream);
+ float GetCacheTime(CActiveAEStream *stream);
+ float GetCacheTotal(CActiveAEStream *stream);
+ float GetWaterLevel();
+ void SetSuspended(bool state);
+ void SetSinkCacheTotal(float time) { m_sinkCacheTotal = time; }
+ bool IsSuspended();
+ CCriticalSection *GetLock() { return &m_lock; }
+protected:
+ float m_sinkDelay;
+ float m_sinkCacheTotal;
+ int m_bufferedSamples;
+ unsigned int m_sinkSampleRate;
+ unsigned int m_sinkUpdate;
+ bool m_suspended;
+ CCriticalSection m_lock;
+};
+
+#if defined(HAS_GLX) || defined(TARGET_DARWIN_OSX)
+class CActiveAE : public IAE, public IDispResource, private CThread
+#else
+class CActiveAE : public IAE, private CThread
+#endif
+{
+protected:
+ friend class ::CAEFactory;
+ friend class CActiveAESound;
+ friend class CActiveAEStream;
+ friend class CSoundPacket;
+ friend class CActiveAEBufferPoolResample;
+ CActiveAE();
+ virtual ~CActiveAE();
+ virtual bool Initialize();
+
+public:
+ virtual void Shutdown();
+ virtual bool Suspend();
+ virtual bool Resume();
+ virtual bool IsSuspended();
+ virtual void OnSettingsChange(const std::string& setting);
+
+ virtual float GetVolume();
+ virtual void SetVolume(const float volume);
+ virtual void SetMute(const bool enabled);
+ virtual bool IsMuted();
+ virtual void SetSoundMode(const int mode);
+
+ /* returns a new stream for data in the specified format */
+ virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options = 0);
+ virtual IAEStream *FreeStream(IAEStream *stream);
+
+ /* returns a new sound object */
+ virtual IAESound *MakeSound(const std::string& file);
+ virtual void FreeSound(IAESound *sound);
+
+ virtual void GarbageCollect() {};
+
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
+ virtual std::string GetDefaultDevice(bool passthrough);
+ virtual bool SupportsRaw();
+ virtual bool SupportsDrain();
+ virtual bool SupportsQualityLevel(enum AEQuality level);
+
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback);
+ virtual void UnregisterAudioCallback();
+
+ virtual void OnLostDevice();
+ virtual void OnResetDevice();
+
+protected:
+ void PlaySound(CActiveAESound *sound);
+ uint8_t **AllocSoundSample(SampleConfig &config, int &samples, int &bytes_per_sample, int &planes, int &linesize);
+ void FreeSoundSample(uint8_t **data);
+ float GetDelay(CActiveAEStream *stream) { return m_stats.GetDelay(stream); }
+ float GetCacheTime(CActiveAEStream *stream) { return m_stats.GetCacheTime(stream); }
+ float GetCacheTotal(CActiveAEStream *stream) { return m_stats.GetCacheTotal(stream); }
+ void FlushStream(CActiveAEStream *stream);
+ void PauseStream(CActiveAEStream *stream, bool pause);
+ void StopSound(CActiveAESound *sound);
+ void SetStreamAmplification(CActiveAEStream *stream, float amplify);
+ void SetStreamReplaygain(CActiveAEStream *stream, float rgain);
+ void SetStreamVolume(CActiveAEStream *stream, float volume);
+ void SetStreamResampleRatio(CActiveAEStream *stream, double ratio);
+ void SetStreamFade(CActiveAEStream *stream, float from, float target, unsigned int millis);
+
+protected:
+ void Process();
+ void StateMachine(int signal, Protocol *port, Message *msg);
+ bool InitSink();
+ void DrainSink();
+ void UnconfigureSink();
+ void Start();
+ void Dispose();
+ void LoadSettings();
+ bool NeedReconfigureBuffers();
+ bool NeedReconfigureSink();
+ void ApplySettingsToFormat(AEAudioFormat &format, AudioSettings &settings, bool setmode = false);
+ void Configure(AEAudioFormat *desiredFmt = NULL);
+ CActiveAEStream* CreateStream(MsgStreamNew *streamMsg);
+ void DiscardStream(CActiveAEStream *stream);
+ void SFlushStream(CActiveAEStream *stream);
+ void ClearDiscardedBuffers();
+ void SStopSound(CActiveAESound *sound);
+ void DiscardSound(CActiveAESound *sound);
+ float CalcStreamAmplification(CActiveAEStream *stream, CSampleBuffer *buf);
+ void ChangeResampleQuality();
+
+ bool RunStages();
+ bool HasWork();
+
+ void ResampleSounds();
+ bool ResampleSound(CActiveAESound *sound);
+ void MixSounds(CSoundPacket &dstSample);
+ void Deamplify(CSoundPacket &dstSample);
+
+ CEvent m_inMsgEvent;
+ CEvent m_outMsgEvent;
+ CActiveAEControlProtocol m_controlPort;
+ CActiveAEDataProtocol m_dataPort;
+ int m_state;
+ bool m_bStateMachineSelfTrigger;
+ int m_extTimeout;
+ bool m_extError;
+ bool m_extDrain;
+ XbmcThreads::EndTime m_extDrainTimer;
+ bool m_extDeferData;
+
+ enum
+ {
+ MODE_RAW,
+ MODE_TRANSCODE,
+ MODE_PCM
+ }m_mode;
+
+ CActiveAESink m_sink;
+ AEAudioFormat m_sinkFormat;
+ AEAudioFormat m_sinkRequestFormat;
+ AEAudioFormat m_encoderFormat;
+ AEAudioFormat m_internalFormat;
+ AudioSettings m_settings;
+ CEngineStats m_stats;
+ IAEEncoder *m_encoder;
+
+ // buffers
+ CActiveAEBufferPoolResample *m_sinkBuffers;
+ CActiveAEBufferPoolResample *m_vizBuffers;
+ CActiveAEBufferPool *m_silenceBuffers; // needed to drive gui sounds if we have no streams
+ CActiveAEBufferPool *m_encoderBuffers;
+
+ // streams
+ std::list<CActiveAEStream*> m_streams;
+ std::list<CActiveAEBufferPool*> m_discardBufferPools;
+
+ // gui sounds
+ struct SoundState
+ {
+ CActiveAESound *sound;
+ int samples_played;
+ };
+ std::list<SoundState> m_sounds_playing;
+ std::vector<CActiveAESound*> m_sounds;
+ int m_soundMode;
+
+ float m_volume;
+ bool m_muted;
+ bool m_sinkHasVolume;
+
+ // viz
+ IAudioCallback *m_audioCallback;
+ bool m_vizInitialized;
+ CCriticalSection m_vizLock;
+
+ // ffmpeg
+ DllAvFormat m_dllAvFormat;
+ DllAvCodec m_dllAvCodec;
+ DllAvUtil m_dllAvUtil;
+
+ // polled via the interface
+ float m_aeVolume;
+ bool m_aeMuted;
+};
+};
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
new file mode 100644
index 0000000000..03fb16f9f1
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ActiveAEBuffer.h"
+#include "AEFactory.h"
+#include "ActiveAE.h"
+
+using namespace ActiveAE;
+
+/* typecast AE to CActiveAE */
+#define AE (*((CActiveAE*)CAEFactory::GetEngine()))
+
+CSoundPacket::CSoundPacket(SampleConfig conf, int samples) : config(conf)
+{
+ data = AE.AllocSoundSample(config, samples, bytes_per_sample, planes, linesize);
+ max_nb_samples = samples;
+ nb_samples = 0;
+}
+
+CSoundPacket::~CSoundPacket()
+{
+ if (data)
+ AE.FreeSoundSample(data);
+}
+
+CSampleBuffer::CSampleBuffer() : pkt(NULL), pool(NULL)
+{
+ refCount = 0;
+}
+
+CSampleBuffer::~CSampleBuffer()
+{
+ delete pkt;
+}
+
+CSampleBuffer* CSampleBuffer::Acquire()
+{
+ refCount++;
+ return this;
+}
+
+void CSampleBuffer::Return()
+{
+ refCount--;
+ if (pool && refCount <= 0)
+ pool->ReturnBuffer(this);
+}
+
+CActiveAEBufferPool::CActiveAEBufferPool(AEAudioFormat format)
+{
+ m_format = format;
+ if (AE_IS_RAW(m_format.m_dataFormat))
+ m_format.m_dataFormat = AE_FMT_S16NE;
+}
+
+CActiveAEBufferPool::~CActiveAEBufferPool()
+{
+ CSampleBuffer *buffer;
+ while(!m_allSamples.empty())
+ {
+ buffer = m_allSamples.front();
+ m_allSamples.pop_front();
+ delete buffer;
+ }
+}
+
+CSampleBuffer* CActiveAEBufferPool::GetFreeBuffer()
+{
+ CSampleBuffer* buf = NULL;
+
+ if (!m_freeSamples.empty())
+ {
+ buf = m_freeSamples.front();
+ m_freeSamples.pop_front();
+ buf->refCount = 1;
+ }
+ return buf;
+}
+
+void CActiveAEBufferPool::ReturnBuffer(CSampleBuffer *buffer)
+{
+ buffer->pkt->nb_samples = 0;
+ m_freeSamples.push_back(buffer);
+}
+
+bool CActiveAEBufferPool::Create(unsigned int totaltime)
+{
+ CSampleBuffer *buffer;
+ SampleConfig config;
+ config.fmt = CActiveAEResample::GetAVSampleFormat(m_format.m_dataFormat);
+ config.channels = m_format.m_channelLayout.Count();
+ config.sample_rate = m_format.m_sampleRate;
+ config.channel_layout = CActiveAEResample::GetAVChannelLayout(m_format.m_channelLayout);
+
+ unsigned int time = 0;
+ unsigned int buffertime = (m_format.m_frames*1000) / m_format.m_sampleRate;
+ unsigned int n = 0;
+ while (time < totaltime || n < 5)
+ {
+ buffer = new CSampleBuffer();
+ buffer->pool = this;
+ buffer->pkt = new CSoundPacket(config, m_format.m_frames);
+
+ m_allSamples.push_back(buffer);
+ m_freeSamples.push_back(buffer);
+ time += buffertime;
+ n++;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+
+CActiveAEBufferPoolResample::CActiveAEBufferPoolResample(AEAudioFormat inputFormat, AEAudioFormat outputFormat, AEQuality quality)
+ : CActiveAEBufferPool(outputFormat)
+{
+ m_inputFormat = inputFormat;
+ if (AE_IS_RAW(m_inputFormat.m_dataFormat))
+ m_inputFormat.m_dataFormat = AE_FMT_S16NE;
+ m_resampler = NULL;
+ m_fillPackets = false;
+ m_drain = false;
+ m_empty = true;
+ m_procSample = NULL;
+ m_resampleRatio = 1.0;
+ m_resampleQuality = quality;
+ m_changeResampler = false;
+}
+
+CActiveAEBufferPoolResample::~CActiveAEBufferPoolResample()
+{
+ delete m_resampler;
+}
+
+bool CActiveAEBufferPoolResample::Create(unsigned int totaltime, bool remap)
+{
+ CActiveAEBufferPool::Create(totaltime);
+
+ if (m_inputFormat.m_channelLayout != m_format.m_channelLayout ||
+ m_inputFormat.m_sampleRate != m_format.m_sampleRate ||
+ m_inputFormat.m_dataFormat != m_format.m_dataFormat)
+ {
+ m_resampler = new CActiveAEResample();
+ m_resampler->Init(CActiveAEResample::GetAVChannelLayout(m_format.m_channelLayout),
+ m_format.m_channelLayout.Count(),
+ m_format.m_sampleRate,
+ CActiveAEResample::GetAVSampleFormat(m_format.m_dataFormat),
+ CActiveAEResample::GetAVChannelLayout(m_inputFormat.m_channelLayout),
+ m_inputFormat.m_channelLayout.Count(),
+ m_inputFormat.m_sampleRate,
+ CActiveAEResample::GetAVSampleFormat(m_inputFormat.m_dataFormat),
+ remap ? &m_format.m_channelLayout : NULL,
+ m_resampleQuality);
+ }
+
+ // store output sampling rate, needed when ratio gets changed
+ m_outSampleRate = m_format.m_sampleRate;
+
+ return true;
+}
+
+void CActiveAEBufferPoolResample::ChangeResampler()
+{
+ m_outSampleRate = m_format.m_sampleRate * m_resampleRatio;
+
+ delete m_resampler;
+
+ m_resampler = new CActiveAEResample();
+ m_resampler->Init(CActiveAEResample::GetAVChannelLayout(m_format.m_channelLayout),
+ m_format.m_channelLayout.Count(),
+ m_outSampleRate,
+ CActiveAEResample::GetAVSampleFormat(m_format.m_dataFormat),
+ CActiveAEResample::GetAVChannelLayout(m_inputFormat.m_channelLayout),
+ m_inputFormat.m_channelLayout.Count(),
+ m_inputFormat.m_sampleRate,
+ CActiveAEResample::GetAVSampleFormat(m_inputFormat.m_dataFormat),
+ NULL,
+ m_resampleQuality);
+
+ m_changeResampler = false;
+}
+
+bool CActiveAEBufferPoolResample::ResampleBuffers(unsigned int timestamp)
+{
+ bool busy = false;
+ CSampleBuffer *in;
+
+ if (!m_resampler)
+ {
+ if (m_changeResampler)
+ {
+ ChangeResampler();
+ return true;
+ }
+ while(!m_inputSamples.empty())
+ {
+ in = m_inputSamples.front();
+ m_inputSamples.pop_front();
+ in->timestamp = timestamp;
+ m_outputSamples.push_back(in);
+ busy = true;
+ }
+ }
+ else if (m_procSample || !m_freeSamples.empty())
+ {
+ // GetBufferedSamples is not accurate because of rounding errors
+ int out_samples = m_resampler->GetBufferedSamples();
+ int free_samples;
+ if (m_procSample)
+ free_samples = m_procSample->pkt->max_nb_samples - m_procSample->pkt->nb_samples;
+ else
+ free_samples = m_format.m_frames;
+
+ bool skipInput = false;
+ // avoid that ffmpeg resample buffer grows too large
+ if (out_samples > free_samples * 2 && !m_empty)
+ skipInput = true;
+
+ bool hasInput = !m_inputSamples.empty();
+
+ if (hasInput || skipInput || m_drain || m_changeResampler)
+ {
+ if (!m_procSample)
+ {
+ m_procSample = GetFreeBuffer();
+ }
+
+ if (hasInput && !skipInput && !m_changeResampler)
+ {
+ in = m_inputSamples.front();
+ m_inputSamples.pop_front();
+ }
+ else
+ in = NULL;
+
+ int start = m_procSample->pkt->nb_samples *
+ m_procSample->pkt->bytes_per_sample *
+ m_procSample->pkt->config.channels /
+ m_procSample->pkt->planes;
+
+ for(int i=0; i<m_procSample->pkt->planes; i++)
+ {
+ m_planes[i] = m_procSample->pkt->data[i] + start;
+ }
+
+ out_samples = m_resampler->Resample(m_planes,
+ m_procSample->pkt->max_nb_samples - m_procSample->pkt->nb_samples,
+ in ? in->pkt->data : NULL,
+ in ? in->pkt->nb_samples : 0);
+ m_procSample->pkt->nb_samples += out_samples;
+ busy = true;
+ m_empty = (out_samples == 0);
+
+ if ((m_drain || m_changeResampler) && m_empty)
+ {
+ if (m_fillPackets && m_procSample->pkt->nb_samples != 0)
+ {
+ // pad with zero
+ start = m_procSample->pkt->nb_samples *
+ m_procSample->pkt->bytes_per_sample *
+ m_procSample->pkt->config.channels /
+ m_procSample->pkt->planes;
+ for(int i=0; i<m_procSample->pkt->planes; i++)
+ {
+ memset(m_procSample->pkt->data[i]+start, 0, m_procSample->pkt->linesize-start);
+ }
+ }
+ m_procSample->timestamp = timestamp;
+
+ // check if draining is finished
+ if (m_drain && m_procSample->pkt->nb_samples == 0)
+ {
+ m_procSample->Return();
+ busy = false;
+ }
+ else
+ m_outputSamples.push_back(m_procSample);
+
+ m_procSample = NULL;
+ if (m_changeResampler)
+ ChangeResampler();
+ }
+ // some methods like encode require completely filled packets
+ else if (!m_fillPackets || (m_procSample->pkt->nb_samples == m_procSample->pkt->max_nb_samples))
+ {
+ m_procSample->timestamp = timestamp;
+ m_outputSamples.push_back(m_procSample);
+ m_procSample = NULL;
+ }
+
+ if (in)
+ in->Return();
+ }
+ }
+ return busy;
+}
+
+float CActiveAEBufferPoolResample::GetDelay()
+{
+ float delay = 0;
+ std::deque<CSampleBuffer*>::iterator itBuf;
+
+ if (m_procSample)
+ delay += m_procSample->pkt->nb_samples / m_procSample->pkt->config.sample_rate;
+
+ for(itBuf=m_inputSamples.begin(); itBuf!=m_inputSamples.end(); ++itBuf)
+ {
+ delay += (float)(*itBuf)->pkt->nb_samples / (*itBuf)->pkt->config.sample_rate;
+ }
+
+ for(itBuf=m_outputSamples.begin(); itBuf!=m_outputSamples.end(); ++itBuf)
+ {
+ delay += (float)(*itBuf)->pkt->nb_samples / (*itBuf)->pkt->config.sample_rate;
+ }
+
+ if (m_resampler)
+ {
+ int samples = m_resampler->GetBufferedSamples();
+ delay += (float)samples / m_outSampleRate;
+ }
+
+ return delay;
+}
+
+void CActiveAEBufferPoolResample::Flush()
+{
+ if (m_procSample)
+ {
+ m_procSample->Return();
+ m_procSample = NULL;
+ }
+ while (!m_inputSamples.empty())
+ {
+ m_inputSamples.front()->Return();
+ m_inputSamples.pop_front();
+ }
+ while (!m_outputSamples.empty())
+ {
+ m_outputSamples.front()->Return();
+ m_outputSamples.pop_front();
+ }
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.h
new file mode 100644
index 0000000000..5b42168f7f
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.h
@@ -0,0 +1,112 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "DllAvUtil.h"
+#include "DllSwResample.h"
+#include "AEAudioFormat.h"
+#include "Interfaces/AE.h"
+#include <deque>
+
+namespace ActiveAE
+{
+
+struct SampleConfig
+{
+ AVSampleFormat fmt;
+ uint64_t channel_layout;
+ int channels;
+ int sample_rate;
+};
+
+/**
+ * the variables here follow ffmpeg naming
+ */
+class CSoundPacket
+{
+public:
+ CSoundPacket(SampleConfig conf, int samples);
+ ~CSoundPacket();
+ uint8_t **data; // array with pointers to planes of data
+ SampleConfig config;
+ AEDataFormat internal_format; // used when carrying pass through
+ int bytes_per_sample; // bytes per sample and per channel
+ int linesize; // see ffmpeg, required for planar formats
+ int planes; // 1 for non planar formats, #channels for planar
+ int nb_samples; // number of frames used
+ int max_nb_samples; // max number of frames this packet can hold
+};
+
+class CActiveAEBufferPool;
+
+class CSampleBuffer
+{
+public:
+ CSampleBuffer();
+ ~CSampleBuffer();
+ CSampleBuffer *Acquire();
+ void Return();
+ CSoundPacket *pkt;
+ CActiveAEBufferPool *pool;
+ unsigned int timestamp;
+ int refCount;
+};
+
+class CActiveAEBufferPool
+{
+public:
+ CActiveAEBufferPool(AEAudioFormat format);
+ virtual ~CActiveAEBufferPool();
+ virtual bool Create(unsigned int totaltime);
+ CSampleBuffer *GetFreeBuffer();
+ void ReturnBuffer(CSampleBuffer *buffer);
+ AEAudioFormat m_format;
+ std::deque<CSampleBuffer*> m_allSamples;
+ std::deque<CSampleBuffer*> m_freeSamples;
+};
+
+class CActiveAEResample;
+
+class CActiveAEBufferPoolResample : public CActiveAEBufferPool
+{
+public:
+ CActiveAEBufferPoolResample(AEAudioFormat inputFormat, AEAudioFormat outputFormat, AEQuality quality);
+ virtual ~CActiveAEBufferPoolResample();
+ virtual bool Create(unsigned int totaltime, bool remap);
+ void ChangeResampler();
+ bool ResampleBuffers(unsigned int timestamp = 0);
+ float GetDelay();
+ void Flush();
+ AEAudioFormat m_inputFormat;
+ std::deque<CSampleBuffer*> m_inputSamples;
+ std::deque<CSampleBuffer*> m_outputSamples;
+ CSampleBuffer *m_procSample;
+ CActiveAEResample *m_resampler;
+ uint8_t *m_planes[16];
+ bool m_fillPackets;
+ bool m_drain;
+ bool m_empty;
+ bool m_changeResampler;
+ double m_resampleRatio;
+ AEQuality m_resampleQuality;
+ unsigned int m_outSampleRate;
+};
+
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp
new file mode 100644
index 0000000000..5678ebc606
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ActiveAEResample.h"
+
+using namespace ActiveAE;
+
+CActiveAEResample::CActiveAEResample()
+{
+ m_pContext = NULL;
+}
+
+CActiveAEResample::~CActiveAEResample()
+{
+ if (m_pContext)
+ m_dllSwResample.swr_free(&m_pContext);
+
+ m_dllAvUtil.Unload();
+ m_dllSwResample.Unload();
+}
+
+bool CActiveAEResample::Init(uint64_t dst_chan_layout, int dst_channels, int dst_rate, AVSampleFormat dst_fmt, uint64_t src_chan_layout, int src_channels, int src_rate, AVSampleFormat src_fmt, CAEChannelInfo *remapLayout, AEQuality quality)
+{
+ if (!m_dllAvUtil.Load() || !m_dllSwResample.Load())
+ return false;
+
+ m_dst_chan_layout = dst_chan_layout;
+ m_dst_channels = dst_channels;
+ m_dst_rate = dst_rate;
+ m_dst_fmt = dst_fmt;
+ m_src_chan_layout = src_chan_layout;
+ m_src_channels = src_channels;
+ m_src_rate = src_rate;
+ m_src_fmt = src_fmt;
+
+ if (m_dst_chan_layout == 0)
+ m_dst_chan_layout = m_dllAvUtil.av_get_default_channel_layout(m_dst_channels);
+ if (m_src_chan_layout == 0)
+ m_src_chan_layout = m_dllAvUtil.av_get_default_channel_layout(m_src_channels);
+
+ m_pContext = m_dllSwResample.swr_alloc_set_opts(NULL, m_dst_chan_layout, m_dst_fmt, m_dst_rate,
+ m_src_chan_layout, m_src_fmt, m_src_rate,
+ 0, NULL);
+ if(quality == AE_QUALITY_HIGH)
+ {
+ m_dllAvUtil.av_opt_set_double(m_pContext, "cutoff", 1.0, 0);
+ m_dllAvUtil.av_opt_set_int(m_pContext,"filter_size", 256, 0);
+ }
+ else if(quality == AE_QUALITY_MID)
+ {
+ // 0.97 is default cutoff so use (1.0 - 0.97) / 2.0 + 0.97
+ m_dllAvUtil.av_opt_set_double(m_pContext, "cutoff", 0.985, 0);
+ m_dllAvUtil.av_opt_set_int(m_pContext,"filter_size", 64, 0);
+ }
+ else if(quality == AE_QUALITY_LOW)
+ {
+ m_dllAvUtil.av_opt_set_double(m_pContext, "cutoff", 0.97, 0);
+ m_dllAvUtil.av_opt_set_int(m_pContext,"filter_size", 32, 0);
+ }
+
+ if(!m_pContext)
+ {
+ CLog::Log(LOGERROR, "CActiveAEResample::Init - create context failed");
+ return false;
+ }
+ if (remapLayout)
+ {
+ // one-to-one mapping of channels
+ // remapLayout is the layout of the sink, if the channel is in our src layout
+ // the channel is mapped by setting coef 1.0
+ memset(m_rematrix, 0, sizeof(m_rematrix));
+ for (unsigned int out=0; out<remapLayout->Count(); out++)
+ {
+ int idx = GetAVChannelIndex((*remapLayout)[out], m_src_chan_layout);
+ if (idx >= 0)
+ {
+ m_rematrix[out][idx] = 1.0;
+ }
+ }
+
+ if (m_dllSwResample.swr_set_matrix(m_pContext, (const double*)m_rematrix, AE_CH_MAX) < 0)
+ {
+ CLog::Log(LOGERROR, "CActiveAEResample::Init - setting channel matrix failed");
+ return false;
+ }
+ }
+ if(m_dllSwResample.swr_init(m_pContext) < 0)
+ {
+ CLog::Log(LOGERROR, "CActiveAEResample::Init - init resampler failed");
+ return false;
+ }
+ return true;
+}
+
+int CActiveAEResample::Resample(uint8_t **dst_buffer, int dst_samples, uint8_t **src_buffer, int src_samples)
+{
+ int ret = m_dllSwResample.swr_convert(m_pContext, dst_buffer, dst_samples, (const uint8_t**)src_buffer, src_samples);
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "CActiveAEResample::Resample - resample failed");
+ return 0;
+ }
+ return ret;
+}
+
+int64_t CActiveAEResample::GetDelay(int64_t base)
+{
+ return m_dllSwResample.swr_get_delay(m_pContext, base);
+}
+
+int CActiveAEResample::GetBufferedSamples()
+{
+ return m_dllAvUtil.av_rescale_rnd(m_dllSwResample.swr_get_delay(m_pContext, m_src_rate),
+ m_dst_rate, m_src_rate, AV_ROUND_UP);
+}
+
+int CActiveAEResample::CalcDstSampleCount(int src_samples, int dst_rate, int src_rate)
+{
+ return m_dllAvUtil.av_rescale_rnd(src_samples, dst_rate, src_rate, AV_ROUND_UP);
+}
+
+int CActiveAEResample::GetSrcBufferSize(int samples)
+{
+ return m_dllAvUtil.av_samples_get_buffer_size(NULL, m_src_channels, samples, m_src_fmt, 1);
+}
+
+int CActiveAEResample::GetDstBufferSize(int samples)
+{
+ return m_dllAvUtil.av_samples_get_buffer_size(NULL, m_dst_channels, samples, m_dst_fmt, 1);
+}
+
+uint64_t CActiveAEResample::GetAVChannelLayout(CAEChannelInfo &info)
+{
+ uint64_t channelLayout = 0;
+ if (info.HasChannel(AE_CH_FL)) channelLayout |= AV_CH_FRONT_LEFT;
+ if (info.HasChannel(AE_CH_FR)) channelLayout |= AV_CH_FRONT_RIGHT;
+ if (info.HasChannel(AE_CH_FC)) channelLayout |= AV_CH_FRONT_CENTER;
+ if (info.HasChannel(AE_CH_LFE)) channelLayout |= AV_CH_LOW_FREQUENCY;
+ if (info.HasChannel(AE_CH_BL)) channelLayout |= AV_CH_BACK_LEFT;
+ if (info.HasChannel(AE_CH_BR)) channelLayout |= AV_CH_BACK_RIGHT;
+ if (info.HasChannel(AE_CH_FLOC)) channelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
+ if (info.HasChannel(AE_CH_FROC)) channelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
+ if (info.HasChannel(AE_CH_BC)) channelLayout |= AV_CH_BACK_CENTER;
+ if (info.HasChannel(AE_CH_SL)) channelLayout |= AV_CH_SIDE_LEFT;
+ if (info.HasChannel(AE_CH_SR)) channelLayout |= AV_CH_SIDE_RIGHT;
+ if (info.HasChannel(AE_CH_TC)) channelLayout |= AV_CH_TOP_CENTER;
+ if (info.HasChannel(AE_CH_TFL)) channelLayout |= AV_CH_TOP_FRONT_LEFT;
+ if (info.HasChannel(AE_CH_TFC)) channelLayout |= AV_CH_TOP_FRONT_CENTER;
+ if (info.HasChannel(AE_CH_TFR)) channelLayout |= AV_CH_TOP_FRONT_RIGHT;
+ if (info.HasChannel(AE_CH_TBL)) channelLayout |= AV_CH_TOP_BACK_LEFT;
+ if (info.HasChannel(AE_CH_TBC)) channelLayout |= AV_CH_TOP_BACK_CENTER;
+ if (info.HasChannel(AE_CH_TBR)) channelLayout |= AV_CH_TOP_BACK_RIGHT;
+
+ return channelLayout;
+}
+
+//CAEChannelInfo CActiveAEResample::GetAEChannelLayout(uint64_t layout)
+//{
+// CAEChannelInfo channelLayout;
+// channelLayout.Reset();
+//
+// if (layout & AV_CH_FRONT_LEFT ) channelLayout += AE_CH_FL ;
+// if (layout & AV_CH_FRONT_RIGHT ) channelLayout += AE_CH_FR ;
+// if (layout & AV_CH_FRONT_CENTER ) channelLayout += AE_CH_FC ;
+// if (layout & AV_CH_LOW_FREQUENCY ) channelLayout += AE_CH_LFE ;
+// if (layout & AV_CH_BACK_LEFT ) channelLayout += AE_CH_BL ;
+// if (layout & AV_CH_BACK_RIGHT ) channelLayout += AE_CH_BR ;
+// if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) channelLayout += AE_CH_FLOC;
+// if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) channelLayout += AE_CH_FROC;
+// if (layout & AV_CH_BACK_CENTER ) channelLayout += AE_CH_BC ;
+// if (layout & AV_CH_SIDE_LEFT ) channelLayout += AE_CH_SL ;
+// if (layout & AV_CH_SIDE_RIGHT ) channelLayout += AE_CH_SR ;
+// if (layout & AV_CH_TOP_CENTER ) channelLayout += AE_CH_TC ;
+// if (layout & AV_CH_TOP_FRONT_LEFT ) channelLayout += AE_CH_TFL ;
+// if (layout & AV_CH_TOP_FRONT_CENTER ) channelLayout += AE_CH_TFC ;
+// if (layout & AV_CH_TOP_FRONT_RIGHT ) channelLayout += AE_CH_TFR ;
+// if (layout & AV_CH_TOP_BACK_LEFT ) channelLayout += AE_CH_BL ;
+// if (layout & AV_CH_TOP_BACK_CENTER ) channelLayout += AE_CH_BC ;
+// if (layout & AV_CH_TOP_BACK_RIGHT ) channelLayout += AE_CH_BR ;
+//
+// return channelLayout;
+//}
+
+AVSampleFormat CActiveAEResample::GetAVSampleFormat(AEDataFormat format)
+{
+ if (format == AE_FMT_U8) return AV_SAMPLE_FMT_U8;
+ else if (format == AE_FMT_S16NE) return AV_SAMPLE_FMT_S16;
+ else if (format == AE_FMT_S32NE) return AV_SAMPLE_FMT_S32;
+ else if (format == AE_FMT_FLOAT) return AV_SAMPLE_FMT_FLT;
+ else if (format == AE_FMT_DOUBLE) return AV_SAMPLE_FMT_DBL;
+
+ else if (format == AE_FMT_U8P) return AV_SAMPLE_FMT_U8P;
+ else if (format == AE_FMT_S16NEP) return AV_SAMPLE_FMT_S16P;
+ else if (format == AE_FMT_S32NEP) return AV_SAMPLE_FMT_S32P;
+ else if (format == AE_FMT_FLOATP) return AV_SAMPLE_FMT_FLTP;
+ else if (format == AE_FMT_DOUBLEP) return AV_SAMPLE_FMT_DBLP;
+
+ return AV_SAMPLE_FMT_FLT;
+}
+
+AEDataFormat CActiveAEResample::GetAESampleFormat(AVSampleFormat format)
+{
+ if (format == AV_SAMPLE_FMT_U8) return AE_FMT_U8;
+ else if (format == AV_SAMPLE_FMT_S16) return AE_FMT_S16NE;
+ else if (format == AV_SAMPLE_FMT_S32) return AE_FMT_S32NE;
+ else if (format == AV_SAMPLE_FMT_FLT) return AE_FMT_FLOAT;
+ else if (format == AV_SAMPLE_FMT_DBL) return AE_FMT_DOUBLE;
+
+ else if (format == AV_SAMPLE_FMT_U8P) return AE_FMT_U8P;
+ else if (format == AV_SAMPLE_FMT_S16P) return AE_FMT_S16NEP;
+ else if (format == AV_SAMPLE_FMT_S32P) return AE_FMT_S32NEP;
+ else if (format == AV_SAMPLE_FMT_FLTP) return AE_FMT_FLOATP;
+ else if (format == AV_SAMPLE_FMT_DBLP) return AE_FMT_DOUBLEP;
+
+ CLog::Log(LOGERROR, "CActiveAEResample::GetAESampleFormat - format not supported");
+ return AE_FMT_INVALID;
+}
+
+uint64_t CActiveAEResample::GetAVChannel(enum AEChannel aechannel)
+{
+ switch (aechannel)
+ {
+ case AE_CH_FL: return AV_CH_FRONT_LEFT;
+ case AE_CH_FR: return AV_CH_FRONT_RIGHT;
+ case AE_CH_FC: return AV_CH_FRONT_CENTER;
+ case AE_CH_LFE: return AV_CH_LOW_FREQUENCY;
+ case AE_CH_BL: return AV_CH_BACK_LEFT;
+ case AE_CH_BR: return AV_CH_BACK_RIGHT;
+ case AE_CH_FLOC: return AV_CH_FRONT_LEFT_OF_CENTER;
+ case AE_CH_FROC: return AV_CH_FRONT_RIGHT_OF_CENTER;
+ case AE_CH_BC: return AV_CH_BACK_CENTER;
+ case AE_CH_SL: return AV_CH_SIDE_LEFT;
+ case AE_CH_SR: return AV_CH_SIDE_RIGHT;
+ case AE_CH_TC: return AV_CH_TOP_CENTER;
+ case AE_CH_TFL: return AV_CH_TOP_FRONT_LEFT;
+ case AE_CH_TFC: return AV_CH_TOP_FRONT_CENTER;
+ case AE_CH_TFR: return AV_CH_TOP_FRONT_RIGHT;
+ case AE_CH_TBL: return AV_CH_TOP_BACK_LEFT;
+ case AE_CH_TBC: return AV_CH_TOP_BACK_CENTER;
+ case AE_CH_TBR: return AV_CH_TOP_BACK_RIGHT;
+ default:
+ return 0;
+ }
+}
+
+int CActiveAEResample::GetAVChannelIndex(enum AEChannel aechannel, uint64_t layout)
+{
+ return m_dllAvUtil.av_get_channel_layout_channel_index(layout, GetAVChannel(aechannel));
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h
new file mode 100644
index 0000000000..2c5aff1d44
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h
@@ -0,0 +1,62 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "DllAvUtil.h"
+#include "DllSwResample.h"
+#include "Utils/AEChannelInfo.h"
+#include "AEAudioFormat.h"
+#include "ActiveAEBuffer.h"
+#include "Interfaces/AE.h"
+
+namespace ActiveAE
+{
+
+class CActiveAEResample
+{
+public:
+ CActiveAEResample();
+ virtual ~CActiveAEResample();
+ bool Init(uint64_t dst_chan_layout, int dst_channels, int dst_rate, AVSampleFormat dst_fmt, uint64_t src_chan_layout, int src_channels, int src_rate, AVSampleFormat src_fmt, CAEChannelInfo *remapLayout, AEQuality quality);
+ int Resample(uint8_t **dst_buffer, int dst_samples, uint8_t **src_buffer, int src_samples);
+ int64_t GetDelay(int64_t base);
+ int GetBufferedSamples();
+ int CalcDstSampleCount(int src_samples, int dst_rate, int src_rate);
+ int GetSrcBufferSize(int samples);
+ int GetDstBufferSize(int samples);
+ static uint64_t GetAVChannelLayout(CAEChannelInfo &info);
+// static CAEChannelInfo GetAEChannelLayout(uint64_t layout);
+ static AVSampleFormat GetAVSampleFormat(AEDataFormat format);
+ static AEDataFormat GetAESampleFormat(AVSampleFormat format);
+ static uint64_t GetAVChannel(enum AEChannel aechannel);
+ int GetAVChannelIndex(enum AEChannel aechannel, uint64_t layout);
+
+protected:
+ DllAvUtil m_dllAvUtil;
+ DllSwResample m_dllSwResample;
+ uint64_t m_src_chan_layout, m_dst_chan_layout;
+ int m_src_rate, m_dst_rate;
+ int m_src_channels, m_dst_channels;
+ AVSampleFormat m_src_fmt, m_dst_fmt;
+ SwrContext *m_pContext;
+ double m_rematrix[AE_CH_MAX][AE_CH_MAX];
+};
+
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
new file mode 100644
index 0000000000..9af241d764
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
@@ -0,0 +1,863 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sstream>
+
+#include "ActiveAESink.h"
+#include "Utils/AEUtil.h"
+#include "utils/EndianSwap.h"
+#include "ActiveAE.h"
+
+#include "settings/Settings.h"
+
+using namespace ActiveAE;
+
+CActiveAESink::CActiveAESink(CEvent *inMsgEvent) :
+ CThread("AESink"),
+ m_controlPort("SinkControlPort", inMsgEvent, &m_outMsgEvent),
+ m_dataPort("SinkDataPort", inMsgEvent, &m_outMsgEvent)
+{
+ m_inMsgEvent = inMsgEvent;
+ m_sink = NULL;
+ m_stats = NULL;
+ m_convertBuffer = NULL;
+ m_volume = 0.0;
+}
+
+void CActiveAESink::Start()
+{
+ if (!IsRunning())
+ {
+ Create();
+ SetPriority(THREAD_PRIORITY_ABOVE_NORMAL);
+ }
+}
+
+void CActiveAESink::Dispose()
+{
+ m_bStop = true;
+ m_outMsgEvent.Set();
+ StopThread();
+ m_controlPort.Purge();
+ m_dataPort.Purge();
+
+ if (m_sink)
+ {
+ m_sink->Drain();
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ }
+
+ delete m_sampleOfSilence.pkt;
+ m_sampleOfSilence.pkt = NULL;
+
+ delete m_sampleOfNoise.pkt;
+ m_sampleOfNoise.pkt = NULL;
+
+ if (m_convertBuffer)
+ {
+ _aligned_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+}
+
+bool CActiveAESink::IsCompatible(const AEAudioFormat format, const std::string &device)
+{
+ if (!m_sink)
+ return false;
+ return m_sink->IsCompatible(format, device);
+}
+
+bool CActiveAESink::HasVolume()
+{
+ if (!m_sink)
+ return false;
+ return m_sink->HasVolume();
+}
+
+enum SINK_STATES
+{
+ S_TOP = 0, // 0
+ S_TOP_UNCONFIGURED, // 1
+ S_TOP_CONFIGURED, // 2
+ S_TOP_CONFIGURED_SUSPEND, // 3
+ S_TOP_CONFIGURED_IDLE, // 4
+ S_TOP_CONFIGURED_PLAY, // 5
+ S_TOP_CONFIGURED_SILENCE, // 6
+ S_TOP_CONFIGURED_WARMUP, // 7
+};
+
+int SINK_parentStates[] = {
+ -1,
+ 0, //TOP_UNCONFIGURED
+ 0, //TOP_CONFIGURED
+ 2, //TOP_CONFIGURED_SUSPEND
+ 2, //TOP_CONFIGURED_IDLE
+ 2, //TOP_CONFIGURED_PLAY
+ 2, //TOP_CONFIGURED_SILENCE
+ 2, //TOP_CONFIGURED_WARMUP
+};
+
+void CActiveAESink::StateMachine(int signal, Protocol *port, Message *msg)
+{
+ for (int state = m_state; ; state = SINK_parentStates[state])
+ {
+ switch (state)
+ {
+ case S_TOP: // TOP
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::CONFIGURE:
+ SinkConfig *data;
+ data = (SinkConfig*)msg->data;
+ if (data)
+ {
+ m_requestedFormat = data->format;
+ m_stats = data->stats;
+ }
+ m_extError = false;
+ m_extSilence = false;
+ ReturnBuffers();
+ OpenSink();
+
+ if (!m_extError)
+ {
+ m_stats->SetSinkCacheTotal(m_sink->GetCacheTotal());
+ m_state = S_TOP_CONFIGURED_IDLE;
+ m_extTimeout = 10000;
+ msg->Reply(CSinkControlProtocol::ACC, &m_sinkFormat, sizeof(AEAudioFormat));
+ }
+ else
+ {
+ m_state = S_TOP_UNCONFIGURED;
+ msg->Reply(CSinkControlProtocol::ERR);
+ }
+ return;
+
+ case CSinkControlProtocol::UNCONFIGURE:
+ ReturnBuffers();
+ if (m_sink)
+ {
+ m_sink->Drain();
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ }
+ m_state = S_TOP_UNCONFIGURED;
+ msg->Reply(CSinkControlProtocol::ACC);
+ return;
+
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case CSinkDataProtocol::DRAIN:
+ msg->Reply(CSinkDataProtocol::ACC);
+ m_state = S_TOP_UNCONFIGURED;
+ m_extTimeout = 0;
+ return;
+ default:
+ break;
+ }
+ }
+ {
+ std::string portName = port == NULL ? "timer" : port->portName;
+ CLog::Log(LOGWARNING, "CActiveAESink::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state);
+ }
+ return;
+
+ case S_TOP_UNCONFIGURED:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::TIMEOUT:
+ m_extTimeout = 1000;
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case CSinkDataProtocol::SAMPLE:
+ CSampleBuffer *samples;
+ int timeout;
+ samples = *((CSampleBuffer**)msg->data);
+ timeout = 1000*samples->pkt->nb_samples/samples->pkt->config.sample_rate;
+ Sleep(timeout);
+ msg->Reply(CSinkDataProtocol::RETURNSAMPLE, &samples, sizeof(CSampleBuffer*));
+ m_extTimeout = 0;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case S_TOP_CONFIGURED:
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::SILENCEMODE:
+ m_extSilence = *(bool*)msg->data;
+ if (CSettings::Get().GetBool("audiooutput.streamsilence"))
+ m_extSilence = true;
+ if (m_extSilence)
+ {
+ m_extCycleCounter = 5;
+ m_state = S_TOP_CONFIGURED_WARMUP;
+ m_extTimeout = 0;
+ }
+ return;
+ case CSinkControlProtocol::VOLUME:
+ m_volume = *(float*)msg->data;
+ m_sink->SetVolume(m_volume);
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case CSinkDataProtocol::DRAIN:
+ m_sink->Drain();
+ msg->Reply(CSinkDataProtocol::ACC);
+ m_state = S_TOP_CONFIGURED_IDLE;
+ m_extTimeout = 10000;
+ return;
+ case CSinkDataProtocol::SAMPLE:
+ CSampleBuffer *samples;
+ unsigned int delay;
+ samples = *((CSampleBuffer**)msg->data);
+ delay = OutputSamples(samples);
+ msg->Reply(CSinkDataProtocol::RETURNSAMPLE, &samples, sizeof(CSampleBuffer*));
+ if (m_extError)
+ {
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ m_state = S_TOP_CONFIGURED_SUSPEND;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = S_TOP_CONFIGURED_PLAY;
+ m_extTimeout = delay / 2;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case S_TOP_CONFIGURED_SUSPEND:
+ if (port == &m_controlPort)
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::SILENCEMODE:
+ return;
+ case CSinkControlProtocol::VOLUME:
+ m_volume = *(float*)msg->data;
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case CSinkDataProtocol::SAMPLE:
+ m_extError = false;
+ OpenSink();
+ OutputSamples(&m_sampleOfNoise);
+ m_state = S_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ m_bStateMachineSelfTrigger = true;
+ return;
+ case CSinkDataProtocol::DRAIN:
+ msg->Reply(CSinkDataProtocol::ACC);
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::TIMEOUT:
+ m_extTimeout = 10000;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case S_TOP_CONFIGURED_IDLE:
+ if (port == &m_dataPort)
+ {
+ switch (signal)
+ {
+ case CSinkDataProtocol::SAMPLE:
+ OutputSamples(&m_sampleOfNoise);
+ m_state = S_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ m_bStateMachineSelfTrigger = true;
+ return;
+ default:
+ break;
+ }
+ }
+ else if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::TIMEOUT:
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ m_state = S_TOP_CONFIGURED_SUSPEND;
+ m_extTimeout = 10000;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case S_TOP_CONFIGURED_PLAY:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::TIMEOUT:
+ if (m_extSilence)
+ {
+ m_state = S_TOP_CONFIGURED_SILENCE;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_sink->Drain();
+ m_state = S_TOP_CONFIGURED_IDLE;
+ m_extTimeout = 10000;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case S_TOP_CONFIGURED_SILENCE:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::TIMEOUT:
+ unsigned int delay;
+ delay = OutputSamples(&m_sampleOfSilence);
+ m_extCycleCounter--;
+ if (m_extError)
+ {
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ m_state = S_TOP_CONFIGURED_SUSPEND;
+ }
+ else if(m_extCycleCounter <= 0)
+ {
+ m_extCycleCounter = 2;
+ m_state = S_TOP_CONFIGURED_WARMUP;
+ }
+ m_extTimeout = 0;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case S_TOP_CONFIGURED_WARMUP:
+ if (port == NULL) // timeout
+ {
+ switch (signal)
+ {
+ case CSinkControlProtocol::TIMEOUT:
+ unsigned int delay;
+ delay = OutputSamples(&m_sampleOfNoise);
+ m_extCycleCounter--;
+ if (m_extError)
+ {
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ m_state = S_TOP_CONFIGURED_SUSPEND;
+ }
+ else if(m_extCycleCounter <= 0)
+ {
+ m_extCycleCounter = 20;
+ m_state = S_TOP_CONFIGURED_SILENCE;
+ }
+ m_extTimeout = 0;
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default: // we are in no state, should not happen
+ CLog::Log(LOGERROR, "CActiveSink::%s - no valid state: %d", __FUNCTION__, m_state);
+ return;
+ }
+ } // for
+}
+
+void CActiveAESink::Process()
+{
+ Message *msg = NULL;
+ Protocol *port = NULL;
+ bool gotMsg;
+
+ m_state = S_TOP_UNCONFIGURED;
+ m_extTimeout = 1000;
+ m_bStateMachineSelfTrigger = false;
+
+ while (!m_bStop)
+ {
+ gotMsg = false;
+
+ if (m_bStateMachineSelfTrigger)
+ {
+ m_bStateMachineSelfTrigger = false;
+ // self trigger state machine
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ continue;
+ }
+ // check control port
+ else if (m_controlPort.ReceiveOutMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_controlPort;
+ }
+ // check data port
+ else if (m_dataPort.ReceiveOutMessage(&msg))
+ {
+ gotMsg = true;
+ port = &m_dataPort;
+ }
+
+ if (gotMsg)
+ {
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ continue;
+ }
+
+ // wait for message
+ else if (m_outMsgEvent.WaitMSec(m_extTimeout))
+ {
+ continue;
+ }
+ // time out
+ else
+ {
+ msg = m_controlPort.GetMessage();
+ msg->signal = CSinkControlProtocol::TIMEOUT;
+ port = 0;
+ // signal timeout to state machine
+ StateMachine(msg->signal, port, msg);
+ if (!m_bStateMachineSelfTrigger)
+ {
+ msg->Release();
+ msg = NULL;
+ }
+ }
+ }
+}
+
+void CActiveAESink::EnumerateSinkList()
+{
+ unsigned int c_retry = 5;
+ m_sinkInfoList.clear();
+ CAESinkFactory::EnumerateEx(m_sinkInfoList);
+ while(m_sinkInfoList.size() == 0 && c_retry > 0)
+ {
+ CLog::Log(LOGNOTICE, "No Devices found - retry: %d", c_retry);
+ Sleep(2000);
+ c_retry--;
+ // retry the enumeration
+ CAESinkFactory::EnumerateEx(m_sinkInfoList, true);
+ }
+ CLog::Log(LOGNOTICE, "Found %lu Lists of Devices", m_sinkInfoList.size());
+ PrintSinks();
+}
+
+void CActiveAESink::PrintSinks()
+{
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ CLog::Log(LOGNOTICE, "Enumerated %s devices:", itt->m_sinkName.c_str());
+ int count = 0;
+ for (AEDeviceInfoList::iterator itt2 = itt->m_deviceInfoList.begin(); itt2 != itt->m_deviceInfoList.end(); ++itt2)
+ {
+ CLog::Log(LOGNOTICE, " Device %d", ++count);
+ CAEDeviceInfo& info = *itt2;
+ std::stringstream ss((std::string)info);
+ std::string line;
+ while(std::getline(ss, line, '\n'))
+ CLog::Log(LOGNOTICE, " %s", line.c_str());
+ }
+ }
+}
+
+void CActiveAESink::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
+{
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ AESinkInfo sinkInfo = *itt;
+ for (AEDeviceInfoList::iterator itt2 = sinkInfo.m_deviceInfoList.begin(); itt2 != sinkInfo.m_deviceInfoList.end(); ++itt2)
+ {
+ CAEDeviceInfo devInfo = *itt2;
+ if (passthrough && devInfo.m_deviceType == AE_DEVTYPE_PCM)
+ continue;
+
+ std::string device = sinkInfo.m_sinkName + ":" + devInfo.m_deviceName;
+
+ std::stringstream ss;
+
+ /* add the sink name if we have more then one sink type */
+ if (m_sinkInfoList.size() > 1)
+ ss << sinkInfo.m_sinkName << ": ";
+
+ ss << devInfo.m_displayName;
+ if (!devInfo.m_displayNameExtra.empty())
+ ss << ", " << devInfo.m_displayNameExtra;
+
+ devices.push_back(AEDevice(ss.str(), device));
+ }
+ }
+}
+
+std::string CActiveAESink::GetDefaultDevice(bool passthrough)
+{
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ AESinkInfo sinkInfo = *itt;
+ for (AEDeviceInfoList::iterator itt2 = sinkInfo.m_deviceInfoList.begin(); itt2 != sinkInfo.m_deviceInfoList.end(); ++itt2)
+ {
+ CAEDeviceInfo devInfo = *itt2;
+ if (passthrough && devInfo.m_deviceType == AE_DEVTYPE_PCM)
+ continue;
+
+ std::string device = sinkInfo.m_sinkName + ":" + devInfo.m_deviceName;
+ return device;
+ }
+ }
+ return "default";
+}
+
+void CActiveAESink::GetDeviceFriendlyName(std::string &device)
+{
+ m_deviceFriendlyName = "Device not found";
+ /* Match the device and find its friendly name */
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ AESinkInfo sinkInfo = *itt;
+ for (AEDeviceInfoList::iterator itt2 = sinkInfo.m_deviceInfoList.begin(); itt2 != sinkInfo.m_deviceInfoList.end(); ++itt2)
+ {
+ CAEDeviceInfo& devInfo = *itt2;
+ if (devInfo.m_deviceName == device)
+ {
+ m_deviceFriendlyName = devInfo.m_displayName;
+ break;
+ }
+ }
+ }
+ return;
+}
+
+void CActiveAESink::OpenSink()
+{
+ std::string device, driver;
+ bool passthrough = AE_IS_RAW(m_requestedFormat.m_dataFormat);
+ if (passthrough)
+ device = CSettings::Get().GetString("audiooutput.passthroughdevice");
+ else
+ device = CSettings::Get().GetString("audiooutput.audiodevice");
+
+ CAESinkFactory::ParseDevice(device, driver);
+ if (driver.empty() && m_sink)
+ driver = m_sink->GetName();
+
+ std::string sinkName;
+ if (m_sink)
+ {
+ sinkName = m_sink->GetName();
+ std::transform(sinkName.begin(), sinkName.end(), sinkName.begin(), ::toupper);
+ }
+
+ if (!m_sink || sinkName != driver || !m_sink->IsCompatible(m_requestedFormat, device))
+ {
+ CLog::Log(LOGINFO, "CActiveAE::OpenSink - sink incompatible, re-starting");
+
+ if (m_sink)
+ {
+ m_sink->Drain();
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ }
+
+ // get the display name of the device
+ GetDeviceFriendlyName(device);
+
+ // if we already have a driver, prepend it to the device string
+ if (!driver.empty())
+ device = driver + ":" + device;
+
+ // WARNING: this changes format and does not use passthrough
+ m_sinkFormat = m_requestedFormat;
+ m_sink = CAESinkFactory::Create(device, m_sinkFormat, passthrough);
+
+ if (!m_sink)
+ {
+ m_extError = true;
+ return;
+ }
+
+ m_sink->SetVolume(m_volume);
+
+#ifdef WORDS_BIGENDIAN
+ if (m_sinkFormat.m_dataFormat == AE_FMT_S16BE)
+ m_sinkFormat.m_dataFormat = AE_FMT_S16NE;
+ else if (m_sinkFormat.m_dataFormat == AE_FMT_S32BE)
+ m_sinkFormat.m_dataFormat = AE_FMT_S32NE;
+#else
+ if (m_sinkFormat.m_dataFormat == AE_FMT_S16LE)
+ m_sinkFormat.m_dataFormat = AE_FMT_S16NE;
+ else if (m_sinkFormat.m_dataFormat == AE_FMT_S32LE)
+ m_sinkFormat.m_dataFormat = AE_FMT_S32NE;
+#endif
+
+ CLog::Log(LOGDEBUG, "CActiveAE::OpenSink - %s Initialized:", m_sink->GetName());
+ CLog::Log(LOGDEBUG, " Output Device : %s", m_deviceFriendlyName.c_str());
+ CLog::Log(LOGDEBUG, " Sample Rate : %d", m_sinkFormat.m_sampleRate);
+ CLog::Log(LOGDEBUG, " Sample Format : %s", CAEUtil::DataFormatToStr(m_sinkFormat.m_dataFormat));
+ CLog::Log(LOGDEBUG, " Channel Count : %d", m_sinkFormat.m_channelLayout.Count());
+ CLog::Log(LOGDEBUG, " Channel Layout: %s", ((std::string)m_sinkFormat.m_channelLayout).c_str());
+ CLog::Log(LOGDEBUG, " Frames : %d", m_sinkFormat.m_frames);
+ CLog::Log(LOGDEBUG, " Frame Samples : %d", m_sinkFormat.m_frameSamples);
+ CLog::Log(LOGDEBUG, " Frame Size : %d", m_sinkFormat.m_frameSize);
+ }
+ else
+ CLog::Log(LOGINFO, "CActiveAE::OpenSink - keeping old sink with : %s, %s, %dhz",
+ CAEUtil::DataFormatToStr(m_sinkFormat.m_dataFormat),
+ ((std::string)m_sinkFormat.m_channelLayout).c_str(),
+ m_sinkFormat.m_sampleRate);
+
+ // init sample of silence
+ SampleConfig config;
+ config.fmt = CActiveAEResample::GetAVSampleFormat(m_sinkFormat.m_dataFormat);
+ config.channel_layout = CActiveAEResample::GetAVChannelLayout(m_sinkFormat.m_channelLayout);
+ config.channels = m_sinkFormat.m_channelLayout.Count();
+ config.sample_rate = m_sinkFormat.m_sampleRate;
+ delete m_sampleOfSilence.pkt;
+ m_sampleOfSilence.pkt = new CSoundPacket(config, m_sinkFormat.m_frames);
+ m_sampleOfSilence.pkt->nb_samples = m_sampleOfSilence.pkt->max_nb_samples;
+
+ // init sample of noise
+ delete m_sampleOfNoise.pkt;
+ m_sampleOfNoise.pkt = new CSoundPacket(config, m_sinkFormat.m_frames);
+ m_sampleOfNoise.pkt->nb_samples = m_sampleOfNoise.pkt->max_nb_samples;
+ if (!passthrough)
+ GenerateNoise();
+
+ if (m_convertBuffer)
+ {
+ _aligned_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+ m_convertFn = NULL;
+ m_convertState = CHECK_CONVERT;
+}
+
+void CActiveAESink::ReturnBuffers()
+{
+ Message *msg = NULL;
+ CSampleBuffer *samples;
+ while (m_dataPort.ReceiveOutMessage(&msg))
+ {
+ if (msg->signal == CSinkDataProtocol::SAMPLE)
+ {
+ samples = *((CSampleBuffer**)msg->data);
+ msg->Reply(CSinkDataProtocol::RETURNSAMPLE, &samples, sizeof(CSampleBuffer*));
+ }
+ }
+}
+
+unsigned int CActiveAESink::OutputSamples(CSampleBuffer* samples)
+{
+ uint8_t *buffer = samples->pkt->data[0];
+ unsigned int frames = samples->pkt->nb_samples;
+ unsigned int maxFrames;
+ int retry = 0;
+ int written = 0;
+ double sinkDelay = 0.0;
+
+ switch(m_convertState)
+ {
+ case SKIP_CONVERT:
+ break;
+ case NEED_CONVERT:
+ EnsureConvertBuffer(samples);
+ buffer = Convert(samples);
+ break;
+ case NEED_BYTESWAP:
+ Endian_Swap16_buf((uint16_t *)buffer, (uint16_t *)buffer, frames * samples->pkt->config.channels);
+ break;
+ case CHECK_CONVERT:
+ ConvertInit(samples);
+ if (m_convertState == NEED_CONVERT)
+ buffer = Convert(samples);
+ else if (m_convertState == NEED_BYTESWAP)
+ Endian_Swap16_buf((uint16_t *)buffer, (uint16_t *)buffer, frames * samples->pkt->config.channels);
+ break;
+ default:
+ break;
+ }
+
+ while(frames > 0)
+ {
+ maxFrames = std::min(frames, m_sinkFormat.m_frames);
+ written = m_sink->AddPackets(buffer, maxFrames, true, true);
+ if (written == 0)
+ {
+ Sleep(500*m_sinkFormat.m_frames/m_sinkFormat.m_sampleRate);
+ retry++;
+ if (retry > 4)
+ {
+ m_extError = true;
+ CLog::Log(LOGERROR, "CActiveAESink::OutputSamples - failed");
+ return 0;
+ }
+ else
+ continue;
+ }
+ frames -= written;
+ buffer += written*m_sinkFormat.m_frameSize;
+ sinkDelay = m_sink->GetDelay();
+ m_stats->UpdateSinkDelay(sinkDelay, samples->pool ? written : 0);
+ }
+ return sinkDelay*1000;
+}
+
+void CActiveAESink::ConvertInit(CSampleBuffer* samples)
+{
+ if (CActiveAEResample::GetAESampleFormat(samples->pkt->config.fmt) != m_sinkFormat.m_dataFormat)
+ {
+ m_convertFn = CAEConvert::FrFloat(m_sinkFormat.m_dataFormat);
+ if (m_convertBuffer)
+ _aligned_free(m_convertBuffer);
+ m_convertBufferSampleSize = samples->pkt->max_nb_samples;
+ m_convertBuffer = (uint8_t*)_aligned_malloc(samples->pkt->max_nb_samples * m_sinkFormat.m_channelLayout.Count() * m_sinkFormat.m_frameSize, 16);
+ memset(m_convertBuffer, 0, samples->pkt->max_nb_samples * m_sinkFormat.m_channelLayout.Count() * m_sinkFormat.m_frameSize);
+ m_convertState = NEED_CONVERT;
+ }
+ else if (AE_IS_RAW(m_requestedFormat.m_dataFormat) && CAEUtil::S16NeedsByteSwap(AE_FMT_S16NE, m_sinkFormat.m_dataFormat))
+ {
+ m_convertState = NEED_BYTESWAP;
+ }
+ else
+ m_convertState = SKIP_CONVERT;
+}
+
+void CActiveAESink::EnsureConvertBuffer(CSampleBuffer* samples)
+{
+ if (!m_convertBuffer)
+ return;
+
+ if (samples->pkt->max_nb_samples <= m_convertBufferSampleSize)
+ return;
+
+ _aligned_free(m_convertBuffer);
+ m_convertBufferSampleSize = samples->pkt->max_nb_samples;
+ m_convertBuffer = (uint8_t*)_aligned_malloc(samples->pkt->max_nb_samples * m_sinkFormat.m_channelLayout.Count() * m_sinkFormat.m_frameSize, 16);
+ memset(m_convertBuffer, 0, samples->pkt->max_nb_samples * m_sinkFormat.m_channelLayout.Count() * m_sinkFormat.m_frameSize);
+}
+
+uint8_t* CActiveAESink::Convert(CSampleBuffer* samples)
+{
+ unsigned int nb_samples = m_convertFn((float*)samples->pkt->data[0], samples->pkt->nb_samples * samples->pkt->config.channels, m_convertBuffer);
+ return m_convertBuffer;
+}
+
+#define PI 3.1415926536f
+
+void CActiveAESink::GenerateNoise()
+{
+ int nb_floats = m_sinkFormat.m_frames*m_sinkFormat.m_channelLayout.Count();
+ float *noise = new float[nb_floats];
+
+ float R1, R2;
+ for(int i=0; i<nb_floats;i++)
+ {
+ do
+ {
+ R1 = (float) rand() / (float) RAND_MAX;
+ R2 = (float) rand() / (float) RAND_MAX;
+ }
+ while(R1 == 0.0f);
+
+ noise[i] = (float) sqrt( -2.0f * log( R1 )) * cos( 2.0f * PI * R2 ) * 0.00001;
+ }
+
+ AEDataFormat fmt = CActiveAEResample::GetAESampleFormat(m_sampleOfNoise.pkt->config.fmt);
+ CAEConvert::AEConvertFrFn convertFn = CAEConvert::FrFloat(fmt);
+ convertFn(noise, nb_floats, m_sampleOfNoise.pkt->data[0]);
+ delete [] noise;
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.h
new file mode 100644
index 0000000000..559179ca6e
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.h
@@ -0,0 +1,138 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "threads/Event.h"
+#include "threads/Thread.h"
+#include "utils/ActorProtocol.h"
+#include "Interfaces/AE.h"
+#include "Interfaces/AESink.h"
+#include "AESinkFactory.h"
+#include "ActiveAEResample.h"
+#include "Utils/AEConvert.h"
+
+namespace ActiveAE
+{
+using namespace Actor;
+
+class CEngineStats;
+
+struct SinkConfig
+{
+ AEAudioFormat format;
+ CEngineStats *stats;
+};
+
+class CSinkControlProtocol : public Protocol
+{
+public:
+ CSinkControlProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {};
+ enum OutSignal
+ {
+ CONFIGURE,
+ UNCONFIGURE,
+ SILENCEMODE,
+ VOLUME,
+ TIMEOUT,
+ };
+ enum InSignal
+ {
+ ACC,
+ ERR,
+ STATS,
+ };
+};
+
+class CSinkDataProtocol : public Protocol
+{
+public:
+ CSinkDataProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {};
+ enum OutSignal
+ {
+ SAMPLE = 0,
+ DRAIN,
+ };
+ enum InSignal
+ {
+ RETURNSAMPLE,
+ ACC,
+ };
+};
+
+class CActiveAESink : private CThread
+{
+public:
+ CActiveAESink(CEvent *inMsgEvent);
+ void EnumerateSinkList();
+ void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
+ std::string GetDefaultDevice(bool passthrough);
+ void Start();
+ void Dispose();
+ bool IsCompatible(const AEAudioFormat format, const std::string &device);
+ bool HasVolume();
+ CSinkControlProtocol m_controlPort;
+ CSinkDataProtocol m_dataPort;
+
+protected:
+ void Process();
+ void StateMachine(int signal, Protocol *port, Message *msg);
+ void PrintSinks();
+ void GetDeviceFriendlyName(std::string &device);
+ void OpenSink();
+ void ReturnBuffers();
+
+ unsigned int OutputSamples(CSampleBuffer* samples);
+ void ConvertInit(CSampleBuffer* samples);
+ inline void EnsureConvertBuffer(CSampleBuffer* samples);
+ inline uint8_t* Convert(CSampleBuffer* samples);
+
+ void GenerateNoise();
+
+ CEvent m_outMsgEvent;
+ CEvent *m_inMsgEvent;
+ int m_state;
+ bool m_bStateMachineSelfTrigger;
+ int m_extTimeout;
+ bool m_extError;
+ bool m_extSilence;
+ int m_extCycleCounter;
+
+ CSampleBuffer m_sampleOfSilence;
+ CSampleBuffer m_sampleOfNoise;
+ uint8_t *m_convertBuffer;
+ int m_convertBufferSampleSize;
+ CAEConvert::AEConvertFrFn m_convertFn;
+ enum
+ {
+ CHECK_CONVERT,
+ NEED_CONVERT,
+ NEED_BYTESWAP,
+ SKIP_CONVERT,
+ } m_convertState;
+
+ std::string m_deviceFriendlyName;
+ AESinkInfoList m_sinkInfoList;
+ IAESink *m_sink;
+ AEAudioFormat m_sinkFormat, m_requestedFormat;
+ CEngineStats *m_stats;
+ float m_volume;
+};
+
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.cpp
new file mode 100644
index 0000000000..fb75ba92e1
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Interfaces/AESound.h"
+
+#include "AEFactory.h"
+#include "AEAudioFormat.h"
+#include "ActiveAE.h"
+#include "ActiveAESound.h"
+#include "utils/log.h"
+#include "DllAvUtil.h"
+
+using namespace ActiveAE;
+using namespace XFILE;
+
+/* typecast AE to CActiveAE */
+#define AE (*((CActiveAE*)CAEFactory::GetEngine()))
+
+CActiveAESound::CActiveAESound(const std::string &filename) :
+ IAESound (filename),
+ m_filename (filename),
+ m_volume (1.0f )
+{
+ m_orig_sound = NULL;
+ m_dst_sound = NULL;
+ m_pFile = NULL;
+}
+
+CActiveAESound::~CActiveAESound()
+{
+ delete m_orig_sound;
+ delete m_dst_sound;
+ Finish();
+}
+
+void CActiveAESound::Play()
+{
+ AE.PlaySound(this);
+}
+
+void CActiveAESound::Stop()
+{
+ AE.StopSound(this);
+}
+
+bool CActiveAESound::IsPlaying()
+{
+ // TODO
+ return false;
+}
+
+uint8_t** CActiveAESound::InitSound(bool orig, SampleConfig config, int nb_samples)
+{
+ CSoundPacket **info;
+ if (orig)
+ info = &m_orig_sound;
+ else
+ info = &m_dst_sound;
+
+ delete *info;
+ *info = new CSoundPacket(config, nb_samples);
+
+ (*info)->nb_samples = 0;
+ m_isConverted = false;
+ return (*info)->data;
+}
+
+bool CActiveAESound::StoreSound(bool orig, uint8_t **buffer, int samples, int linesize)
+{
+ CSoundPacket **info;
+ if (orig)
+ info = &m_orig_sound;
+ else
+ info = &m_dst_sound;
+
+ if ((*info)->nb_samples + samples > (*info)->max_nb_samples)
+ {
+ CLog::Log(LOGERROR, "CActiveAESound::StoreSound - exceeded max samples");
+ return false;
+ }
+
+ int bytes_to_copy = samples * (*info)->bytes_per_sample * (*info)->config.channels;
+ bytes_to_copy /= (*info)->planes;
+ int start = (*info)->nb_samples * (*info)->bytes_per_sample * (*info)->config.channels;
+ start /= (*info)->planes;
+
+ for (int i=0; i<(*info)->planes; i++)
+ {
+ memcpy((*info)->data[i]+start, buffer[i], bytes_to_copy);
+ }
+ (*info)->nb_samples += samples;
+
+ return true;
+}
+
+CSoundPacket *CActiveAESound::GetSound(bool orig)
+{
+ if (orig)
+ return m_orig_sound;
+ else
+ return m_dst_sound;
+}
+
+bool CActiveAESound::Prepare()
+{
+ unsigned int flags = READ_TRUNCATED | READ_CHUNKED;
+ m_pFile = new CFile();
+
+ if (!m_pFile->Open(m_filename, flags))
+ {
+ delete m_pFile;
+ m_pFile = NULL;
+ return false;
+ }
+ m_isSeekPosible = m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL) != 0;
+ m_fileSize = m_pFile->GetLength();
+ return true;
+}
+
+void CActiveAESound::Finish()
+{
+ delete m_pFile;
+ m_pFile = NULL;
+}
+
+int CActiveAESound::GetChunkSize()
+{
+ return m_pFile->GetChunkSize();
+}
+
+int CActiveAESound::Read(void *h, uint8_t* buf, int size)
+{
+ CFile *pFile = static_cast<CActiveAESound*>(h)->m_pFile;
+ return pFile->Read(buf, size);
+}
+
+offset_t CActiveAESound::Seek(void *h, offset_t pos, int whence)
+{
+ CFile* pFile = static_cast<CActiveAESound*>(h)->m_pFile;
+ if(whence == AVSEEK_SIZE)
+ return pFile->GetLength();
+ else
+ return pFile->Seek(pos, whence & ~AVSEEK_FORCE);
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.h
new file mode 100644
index 0000000000..7cdf76cc1c
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESound.h
@@ -0,0 +1,73 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "utils/StdString.h"
+#include "Interfaces/AESound.h"
+#include "ActiveAEResample.h"
+#include "filesystem/File.h"
+
+class DllAvUtil;
+
+namespace ActiveAE
+{
+
+class CActiveAESound : public IAESound
+{
+public:
+ CActiveAESound (const std::string &filename);
+ virtual ~CActiveAESound();
+
+ virtual void Play();
+ virtual void Stop();
+ virtual bool IsPlaying();
+
+ virtual void SetVolume(float volume) { m_volume = std::max(0.0f, std::min(1.0f, volume)); }
+ virtual float GetVolume() { return m_volume; }
+
+ uint8_t** InitSound(bool orig, SampleConfig config, int nb_samples);
+ bool StoreSound(bool orig, uint8_t **buffer, int samples, int linesize);
+ CSoundPacket *GetSound(bool orig);
+
+ bool IsConverted() { return m_isConverted; }
+ void SetConverted(bool state) { m_isConverted = state; }
+
+ bool Prepare();
+ void Finish();
+ int GetChunkSize();
+ int GetFileSize() { return m_fileSize; }
+ bool IsSeekPosible() { return m_isSeekPosible; }
+
+ static int Read(void *h, uint8_t* buf, int size);
+ static offset_t Seek(void *h, offset_t pos, int whence);
+
+protected:
+ std::string m_filename;
+ XFILE::CFile *m_pFile;
+ bool m_isSeekPosible;
+ int m_fileSize;
+ float m_volume;
+
+ CSoundPacket *m_orig_sound;
+ CSoundPacket *m_dst_sound;
+
+ bool m_isConverted;
+};
+}
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp
new file mode 100644
index 0000000000..66f62b70a3
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/MathUtils.h"
+
+#include "AEFactory.h"
+#include "Utils/AEUtil.h"
+
+#include "ActiveAE.h"
+#include "ActiveAEStream.h"
+
+using namespace ActiveAE;
+
+/* typecast AE to CActiveAE */
+#define AE (*((CActiveAE*)CAEFactory::GetEngine()))
+
+
+CActiveAEStream::CActiveAEStream(AEAudioFormat *format)
+{
+ m_format = *format;
+ m_bufferedTime = 0;
+ m_currentBuffer = NULL;
+ m_drain = false;
+ m_paused = false;
+ m_rgain = 1.0;
+ m_volume = 1.0;
+ m_streamSpace = m_format.m_frameSize * m_format.m_frames;
+ m_streamDraining = false;
+ m_streamDrained = false;
+ m_streamFading = false;
+ m_streamFreeBuffers = 0;
+ m_streamIsBuffering = true;
+ m_streamSlave = NULL;
+ m_convertFn = NULL;
+}
+
+CActiveAEStream::~CActiveAEStream()
+{
+}
+
+void CActiveAEStream::IncFreeBuffers()
+{
+ CSingleLock lock(m_streamLock);
+ m_streamFreeBuffers++;
+}
+
+void CActiveAEStream::DecFreeBuffers()
+{
+ CSingleLock lock(m_streamLock);
+ m_streamFreeBuffers--;
+}
+
+void CActiveAEStream::ResetFreeBuffers()
+{
+ CSingleLock lock(m_streamLock);
+ m_streamFreeBuffers = 0;
+}
+
+unsigned int CActiveAEStream::GetSpace()
+{
+ CSingleLock lock(m_streamLock);
+ return m_streamFreeBuffers * m_streamSpace;
+}
+
+unsigned int CActiveAEStream::AddData(void *data, unsigned int size)
+{
+ Message *msg;
+ unsigned int copied = 0;
+ int bytesToCopy = size;
+ while(copied < size)
+ {
+ if (m_currentBuffer)
+ {
+ int start = m_currentBuffer->pkt->nb_samples *
+ m_currentBuffer->pkt->bytes_per_sample *
+ m_currentBuffer->pkt->config.channels /
+ m_currentBuffer->pkt->planes;
+
+ int freeSamples = m_currentBuffer->pkt->max_nb_samples - m_currentBuffer->pkt->nb_samples;
+ int availableSamples = bytesToCopy / m_format.m_frameSize;
+ int space = freeSamples * m_currentBuffer->pkt->bytes_per_sample * m_currentBuffer->pkt->config.channels;
+ int samples = std::min(freeSamples, availableSamples);
+ int bytes = samples * m_format.m_frameSize;
+ //TODO: handle planar formats
+ if (m_convertFn)
+ m_convertFn((uint8_t*)data+copied, samples*m_currentBuffer->pkt->config.channels, (float*)(m_currentBuffer->pkt->data[0] + start));
+ else
+ memcpy(m_currentBuffer->pkt->data[0] + start, (uint8_t*)data+copied, bytes);
+ {
+ CSingleLock lock(*m_statsLock);
+ m_currentBuffer->pkt->nb_samples += samples;
+ m_bufferedTime += (double)samples / m_currentBuffer->pkt->config.sample_rate;
+ }
+ copied += bytes;
+ bytesToCopy -= bytes;
+ if (m_currentBuffer->pkt->nb_samples == m_currentBuffer->pkt->max_nb_samples)
+ {
+ MsgStreamSample msgData;
+ msgData.buffer = m_currentBuffer;
+ msgData.stream = this;
+ m_streamPort->SendOutMessage(CActiveAEDataProtocol::STREAMSAMPLE, &msgData, sizeof(MsgStreamSample));
+ m_currentBuffer = NULL;
+ }
+ continue;
+ }
+ else if (m_streamPort->ReceiveInMessage(&msg))
+ {
+ if (msg->signal == CActiveAEDataProtocol::STREAMBUFFER)
+ {
+ m_currentBuffer = *((CSampleBuffer**)msg->data);
+ msg->Release();
+ DecFreeBuffers();
+ continue;
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "CActiveAEStream::AddData - unknown signal");
+ msg->Release();
+ break;
+ }
+ }
+ if (!m_inMsgEvent.WaitMSec(200))
+ break;
+ }
+ return copied;
+}
+
+double CActiveAEStream::GetDelay()
+{
+ return AE.GetDelay(this);
+}
+
+bool CActiveAEStream::IsBuffering()
+{
+ CSingleLock lock(m_streamLock);
+ return m_streamIsBuffering;
+}
+
+double CActiveAEStream::GetCacheTime()
+{
+ return AE.GetCacheTime(this);
+}
+
+double CActiveAEStream::GetCacheTotal()
+{
+ return AE.GetCacheTotal(this);
+}
+
+void CActiveAEStream::Pause()
+{
+ AE.PauseStream(this, true);
+}
+
+void CActiveAEStream::Resume()
+{
+ AE.PauseStream(this, false);
+}
+
+void CActiveAEStream::Drain(bool wait)
+{
+ Message *msg;
+ CActiveAEStream *stream = this;
+
+ m_streamDraining = true;
+ m_streamDrained = false;
+
+ Message *reply;
+ if (m_streamPort->SendOutMessageSync(CActiveAEDataProtocol::DRAINSTREAM,
+ &reply,2000,
+ &stream, sizeof(CActiveAEStream*)))
+ {
+ bool success = reply->signal == CActiveAEDataProtocol::ACC ? true : false;
+ reply->Release();
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "CActiveAEStream::Drain - no acc");
+ }
+ }
+
+ if (m_currentBuffer)
+ {
+ MsgStreamSample msgData;
+ msgData.buffer = m_currentBuffer;
+ msgData.stream = this;
+ m_streamPort->SendOutMessage(CActiveAEDataProtocol::STREAMSAMPLE, &msgData, sizeof(MsgStreamSample));
+ m_currentBuffer = NULL;
+ }
+
+ XbmcThreads::EndTime timer(2000);
+ while (!timer.IsTimePast())
+ {
+ if (m_streamPort->ReceiveInMessage(&msg))
+ {
+ if (msg->signal == CActiveAEDataProtocol::STREAMBUFFER)
+ {
+ MsgStreamSample msgData;
+ msgData.stream = this;
+ msgData.buffer = *((CSampleBuffer**)msg->data);
+ msg->Reply(CActiveAEDataProtocol::STREAMSAMPLE, &msgData, sizeof(MsgStreamSample));
+ DecFreeBuffers();
+ continue;
+ }
+ else if (msg->signal == CActiveAEDataProtocol::STREAMDRAINED)
+ {
+ msg->Release();
+ return;
+ }
+ }
+ else if (!wait)
+ return;
+
+ m_inMsgEvent.WaitMSec(timer.MillisLeft());
+ }
+ CLog::Log(LOGERROR, "CActiveAEStream::Drain - timeout out");
+}
+
+bool CActiveAEStream::IsDraining()
+{
+ CSingleLock lock(m_streamLock);
+ return m_streamDraining;
+}
+
+bool CActiveAEStream::IsDrained()
+{
+ CSingleLock lock(m_streamLock);
+ return m_streamDrained;
+}
+
+void CActiveAEStream::Flush()
+{
+ if (m_currentBuffer)
+ {
+ MsgStreamSample msgData;
+ m_currentBuffer->pkt->nb_samples = 0;
+ msgData.buffer = m_currentBuffer;
+ msgData.stream = this;
+ m_streamPort->SendOutMessage(CActiveAEDataProtocol::STREAMSAMPLE, &msgData, sizeof(MsgStreamSample));
+ m_currentBuffer = NULL;
+ }
+ AE.FlushStream(this);
+ ResetFreeBuffers();
+}
+
+float CActiveAEStream::GetAmplification()
+{
+ return m_streamAmplify;
+}
+
+void CActiveAEStream::SetAmplification(float amplify)
+{
+ m_streamAmplify = amplify;
+ AE.SetStreamAmplification(this, m_streamAmplify);
+}
+
+float CActiveAEStream::GetReplayGain()
+{
+ return m_streamRgain;
+}
+
+void CActiveAEStream::SetReplayGain(float factor)
+{
+ m_streamRgain = std::max( 0.0f, factor);
+ AE.SetStreamReplaygain(this, m_streamRgain);
+}
+
+float CActiveAEStream::GetVolume()
+{
+ return m_streamVolume;
+}
+
+void CActiveAEStream::SetVolume(float volume)
+{
+ m_streamVolume = std::max( 0.0f, std::min(1.0f, volume));
+ AE.SetStreamVolume(this, m_streamVolume);
+}
+
+double CActiveAEStream::GetResampleRatio()
+{
+ return m_streamResampleRatio;
+}
+
+bool CActiveAEStream::SetResampleRatio(double ratio)
+{
+ m_streamResampleRatio = ratio;
+ AE.SetStreamResampleRatio(this, m_streamResampleRatio);
+ return true;
+}
+
+void CActiveAEStream::FadeVolume(float from, float target, unsigned int time)
+{
+ if (time == 0)
+ return;
+
+ m_streamFading = true;
+ AE.SetStreamFade(this, from, target, time);
+}
+
+bool CActiveAEStream::IsFading()
+{
+ CSingleLock lock(m_streamLock);
+ return m_streamFading;
+}
+
+const unsigned int CActiveAEStream::GetFrameSize() const
+{
+ return m_format.m_frameSize;
+}
+
+const unsigned int CActiveAEStream::GetChannelCount() const
+{
+ return m_format.m_channelLayout.Count();
+}
+
+const unsigned int CActiveAEStream::GetSampleRate() const
+{
+ return m_format.m_sampleRate;
+}
+
+const unsigned int CActiveAEStream::GetEncodedSampleRate() const
+{
+ return m_format.m_encodedRate;
+}
+
+const enum AEDataFormat CActiveAEStream::GetDataFormat() const
+{
+ return m_format.m_dataFormat;
+}
+
+void CActiveAEStream::RegisterAudioCallback(IAudioCallback* pCallback)
+{
+}
+
+void CActiveAEStream::UnRegisterAudioCallback()
+{
+}
+
+void CActiveAEStream::RegisterSlave(IAEStream *slave)
+{
+ CSingleLock lock(m_streamLock);
+ m_streamSlave = slave;
+}
+
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h
new file mode 100644
index 0000000000..bddf1a37b1
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h
@@ -0,0 +1,117 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "AEAudioFormat.h"
+#include "Interfaces/AEStream.h"
+#include "Utils/AELimiter.h"
+#include "Utils/AEConvert.h"
+
+namespace ActiveAE
+{
+
+class CActiveAEStream : public IAEStream
+{
+protected:
+ friend class CActiveAE;
+ friend class CEngineStats;
+ CActiveAEStream(AEAudioFormat *format);
+ virtual ~CActiveAEStream();
+ void FadingFinished();
+ void IncFreeBuffers();
+ void DecFreeBuffers();
+ void ResetFreeBuffers();
+
+public:
+ virtual unsigned int GetSpace();
+ virtual unsigned int AddData(void *data, unsigned int size);
+ virtual double GetDelay();
+ virtual bool IsBuffering();
+ virtual double GetCacheTime();
+ virtual double GetCacheTotal();
+
+ virtual void Pause();
+ virtual void Resume();
+ virtual void Drain(bool wait);
+ virtual bool IsDraining();
+ virtual bool IsDrained();
+ virtual void Flush();
+
+ virtual float GetVolume();
+ virtual float GetReplayGain();
+ virtual float GetAmplification();
+ virtual void SetVolume(float volume);
+ virtual void SetReplayGain(float factor);
+ virtual void SetAmplification(float amplify);
+
+ virtual const unsigned int GetFrameSize() const;
+ virtual const unsigned int GetChannelCount() const;
+
+ virtual const unsigned int GetSampleRate() const ;
+ virtual const unsigned int GetEncodedSampleRate() const;
+ virtual const enum AEDataFormat GetDataFormat() const;
+
+ virtual double GetResampleRatio();
+ virtual bool SetResampleRatio(double ratio);
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback);
+ virtual void UnRegisterAudioCallback();
+ virtual void FadeVolume(float from, float to, unsigned int time);
+ virtual bool IsFading();
+ virtual void RegisterSlave(IAEStream *stream);
+
+protected:
+
+ AEAudioFormat m_format;
+ float m_streamVolume;
+ float m_streamRgain;
+ float m_streamAmplify;
+ double m_streamResampleRatio;
+ unsigned int m_streamSpace;
+ bool m_streamDraining;
+ bool m_streamDrained;
+ bool m_streamFading;
+ int m_streamFreeBuffers;
+ bool m_streamIsBuffering;
+ IAEStream *m_streamSlave;
+ CAEConvert::AEConvertToFn m_convertFn;
+ CCriticalSection m_streamLock;
+
+ // only accessed by engine
+ CActiveAEBufferPool *m_inputBuffers;
+ CActiveAEBufferPoolResample *m_resampleBuffers;
+ std::deque<CSampleBuffer*> m_processingSamples;
+ CSampleBuffer *m_currentBuffer;
+ CActiveAEDataProtocol *m_streamPort;
+ CEvent m_inMsgEvent;
+ CCriticalSection *m_statsLock;
+ bool m_drain;
+ bool m_paused;
+ bool m_started;
+ CAELimiter m_limiter;
+ float m_volume;
+ float m_rgain;
+ float m_bufferedTime;
+ int m_fadingSamples;
+ float m_fadingBase;
+ float m_fadingTarget;
+ int m_fadingTime;
+};
+}
+
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.cpp
index a9422f779d..073769a2e9 100644
--- a/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.cpp
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.cpp
@@ -603,7 +603,7 @@ void CCoreAudioAEStream::Resume()
m_paused = false;
}
-void CCoreAudioAEStream::Drain()
+void CCoreAudioAEStream::Drain(bool wait)
{
m_draining = true;
}
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.h b/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.h
index 18c52eda1e..1366808132 100644
--- a/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.h
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.h
@@ -73,7 +73,7 @@ public:
virtual void Pause();
virtual void Resume();
- virtual void Drain();
+ virtual void Drain(bool wait);
virtual void Flush();
virtual float GetVolume();
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.cpp b/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.cpp
index 443c844be5..e1e19033e3 100644
--- a/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.cpp
+++ b/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.cpp
@@ -386,7 +386,7 @@ void CPulseAEStream::Resume()
m_Paused = Cork(false);
}
-void CPulseAEStream::Drain()
+void CPulseAEStream::Drain(bool wait)
{
if (!m_Initialized)
return;
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.h b/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.h
index 51aeda853a..8d9435b15c 100644
--- a/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.h
+++ b/xbmc/cores/AudioEngine/Engines/PulseAE/PulseAEStream.h
@@ -48,7 +48,7 @@ public:
virtual void Pause ();
virtual void Resume ();
- virtual void Drain ();
+ virtual void Drain (bool wait);
virtual void Flush ();
virtual float GetVolume ();
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp
index 05ee1b0bfb..b5c2da33ba 100644
--- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp
+++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp
@@ -555,7 +555,7 @@ void CSoftAEStream::Resume()
AE.ResumeStream(this);
}
-void CSoftAEStream::Drain()
+void CSoftAEStream::Drain(bool wait)
{
CSingleLock lock(m_lock);
m_draining = true;
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h
index f7410e3223..1475c836f8 100644
--- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h
+++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h
@@ -59,7 +59,7 @@ public:
virtual void Pause ();
virtual void Resume ();
- virtual void Drain ();
+ virtual void Drain (bool wait);
virtual bool IsDraining () { return m_draining; }
virtual bool IsDrained ();
virtual void Flush ();
diff --git a/xbmc/cores/AudioEngine/Interfaces/AE.h b/xbmc/cores/AudioEngine/Interfaces/AE.h
index cbfa8ed399..aa92e83226 100644
--- a/xbmc/cores/AudioEngine/Interfaces/AE.h
+++ b/xbmc/cores/AudioEngine/Interfaces/AE.h
@@ -34,12 +34,28 @@ typedef std::vector<AEDevice> AEDeviceList;
class IAEStream;
class IAESound;
class IAEPacketizer;
+class IAudioCallback;
/* sound options */
#define AE_SOUND_OFF 0 /* disable sounds */
#define AE_SOUND_IDLE 1 /* only play sounds while no streams are running */
#define AE_SOUND_ALWAYS 2 /* always play sounds */
+enum AEQuality
+{
+ AE_QUALITY_UNKNOWN = -1, /* Unset, unknown or incorrect quality level */
+ AE_QUALITY_DEFAULT = 0, /* Engine's default quality level */
+
+ /* Basic quality levels */
+ AE_QUALITY_LOW = 20, /* Low quality level */
+ AE_QUALITY_MID = 30, /* Standard quality level */
+ AE_QUALITY_HIGH = 50, /* Best sound processing quality */
+
+ /* Optional quality levels */
+ AE_QUALITY_REALLYHIGH = 100 /* Uncompromised optional quality level,
+ usually with unmeasurable and unnoticeable improvement */
+};
+
/**
* IAE Interface
*/
@@ -183,5 +199,21 @@ public:
* @returns true if the AudioEngine is capable of RAW output
*/
virtual bool SupportsRaw() { return false; }
+
+ /**
+ * Returns true if the AudioEngine supports drain mode which is not streaming silence when idle
+ * @returns true if the AudioEngine is capable of drain mode
+ */
+ virtual bool SupportsDrain() { return false; }
+
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback) {}
+
+ virtual void UnregisterAudioCallback() {}
+
+ /**
+ * Returns true if AudioEngine supports specified quality level
+ * @return true if specified quality level is supported, otherwise false
+ */
+ virtual bool SupportsQualityLevel(enum AEQuality level) { return false; }
};
diff --git a/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h b/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h
index 8c50fb1008..6e4c257592 100644
--- a/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h
+++ b/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h
@@ -48,9 +48,10 @@ public:
/**
* Called to setup the encoder to accept data in the specified format
* @param format the desired audio format, may be changed to suit the encoder
+ * @param allow_planar_input allow engine to use with planar formats
* @return true on success, false on failure
*/
- virtual bool Initialize(AEAudioFormat &format) = 0;
+ virtual bool Initialize(AEAudioFormat &format, bool allow_planar_input = false) = 0;
/**
* Reset the encoder for new data
@@ -84,6 +85,16 @@ public:
virtual int Encode(float *data, unsigned int frames) = 0;
/**
+ * Encodes the supplied samples into a provided buffer
+ * @param in the PCM samples encoder requested format
+ * @param in_size input buffer size
+ * @param output buffer
+ * @param out_size output buffer size
+ * @return the number of samples consumed
+ */
+ virtual int Encode (uint8_t *in, int in_size, uint8_t *out, int out_size) { return 0; };
+
+ /**
* Get the encoded data
* @param data return pointer to the buffer with the current encoded block
* @return the size in bytes of *data
diff --git a/xbmc/cores/AudioEngine/Interfaces/AESink.h b/xbmc/cores/AudioEngine/Interfaces/AESink.h
index d4fc3b9a84..520d6efb25 100644
--- a/xbmc/cores/AudioEngine/Interfaces/AESink.h
+++ b/xbmc/cores/AudioEngine/Interfaces/AESink.h
@@ -71,7 +71,7 @@ public:
/*
Adds packets to be sent out, this routine MUST block or sleep.
*/
- virtual unsigned int AddPackets(uint8_t *data, unsigned int frames, bool hasAudio) = 0;
+ virtual unsigned int AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false) = 0;
/*
Drain the sink
diff --git a/xbmc/cores/AudioEngine/Interfaces/AEStream.h b/xbmc/cores/AudioEngine/Interfaces/AEStream.h
index 67fa5e2a0f..92d5fef01e 100644
--- a/xbmc/cores/AudioEngine/Interfaces/AEStream.h
+++ b/xbmc/cores/AudioEngine/Interfaces/AEStream.h
@@ -97,7 +97,7 @@ public:
* Start draining the stream
* @note Once called AddData will not consume more data.
*/
- virtual void Drain() = 0;
+ virtual void Drain(bool wait) = 0;
/**
* Returns true if the is stream draining
diff --git a/xbmc/cores/AudioEngine/Makefile.in b/xbmc/cores/AudioEngine/Makefile.in
index 833dfabb70..2e1a83d49d 100644
--- a/xbmc/cores/AudioEngine/Makefile.in
+++ b/xbmc/cores/AudioEngine/Makefile.in
@@ -43,6 +43,13 @@ SRCS += Engines/SoftAE/SoftAE.cpp
SRCS += Engines/SoftAE/SoftAEStream.cpp
SRCS += Engines/SoftAE/SoftAESound.cpp
+SRCS += Engines/ActiveAE/ActiveAE.cpp
+SRCS += Engines/ActiveAE/ActiveAESink.cpp
+SRCS += Engines/ActiveAE/ActiveAEStream.cpp
+SRCS += Engines/ActiveAE/ActiveAESound.cpp
+SRCS += Engines/ActiveAE/ActiveAEResample.cpp
+SRCS += Engines/ActiveAE/ActiveAEBuffer.cpp
+
ifeq (@USE_ANDROID@,1)
SRCS += Sinks/AESinkAUDIOTRACK.cpp
else
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
index 45028d6cb4..4e0603155e 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
@@ -131,19 +131,20 @@ void CAESinkALSA::GetAESParams(AEAudioFormat format, std::string& params)
bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
{
+ CAEChannelInfo channelLayout;
m_initDevice = device;
m_initFormat = format;
/* if we are raw, correct the data format */
if (AE_IS_RAW(format.m_dataFormat))
{
- m_channelLayout = GetChannelLayout(format);
+ channelLayout = GetChannelLayout(format);
format.m_dataFormat = AE_FMT_S16NE;
m_passthrough = true;
}
else
{
- m_channelLayout = GetChannelLayout(format);
+ channelLayout = GetChannelLayout(format);
m_passthrough = false;
}
#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC)
@@ -154,13 +155,13 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
}
#endif
- if (m_channelLayout.Count() == 0)
+ if (channelLayout.Count() == 0)
{
CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout");
return false;
}
- format.m_channelLayout = m_channelLayout;
+ format.m_channelLayout = channelLayout;
AEDeviceType devType = AEDeviceTypeFromName(device);
@@ -176,7 +177,7 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
snd_config_t *config;
snd_config_copy(&config, snd_config);
- if (!OpenPCMDevice(device, AESParams, m_channelLayout.Count(), &m_pcm, config))
+ if (!OpenPCMDevice(device, AESParams, channelLayout.Count(), &m_pcm, config))
{
CLog::Log(LOGERROR, "CAESinkALSA::Initialize - failed to initialize device \"%s\"", device.c_str());
snd_config_delete(config);
@@ -435,10 +436,10 @@ bool CAESinkALSA::InitializeSW(AEAudioFormat &format)
void CAESinkALSA::Deinitialize()
{
- Stop();
-
if (m_pcm)
{
+ snd_pcm_nonblock(m_pcm, 0);
+ Stop();
snd_pcm_close(m_pcm);
m_pcm = NULL;
}
@@ -496,7 +497,7 @@ double CAESinkALSA::GetCacheTotal()
return (double)m_bufferSize * m_formatSampleRateMul;
}
-unsigned int CAESinkALSA::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
+unsigned int CAESinkALSA::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
{
if (!m_pcm)
{
@@ -517,7 +518,14 @@ unsigned int CAESinkALSA::AddPackets(uint8_t *data, unsigned int frames, bool ha
}
if ((unsigned int)ret < frames)
- return 0;
+ if(blocking)
+ {
+ ret = snd_pcm_wait(m_pcm, m_timeout);
+ if (ret < 0)
+ HandleError("snd_pcm_wait", ret);
+ }
+ else
+ return 0;
ret = snd_pcm_writei(m_pcm, (void*)data, frames);
if (ret < 0)
@@ -573,6 +581,7 @@ void CAESinkALSA::Drain()
snd_pcm_nonblock(m_pcm, 0);
snd_pcm_drain(m_pcm);
+ snd_pcm_prepare(m_pcm);
snd_pcm_nonblock(m_pcm, 1);
}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h
index 39e3719b22..e43ea5e392 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h
@@ -47,7 +47,7 @@ public:
virtual double GetDelay ();
virtual double GetCacheTime ();
virtual double GetCacheTotal ();
- virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
virtual void Drain ();
virtual bool SoftSuspend();
virtual bool SoftResume();
@@ -64,7 +64,6 @@ private:
unsigned int m_bufferSize;
double m_formatSampleRateMul;
bool m_passthrough;
- CAEChannelInfo m_channelLayout;
std::string m_device;
snd_pcm_t *m_pcm;
int m_timeout;
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
index 07a4bccdd5..48e488b6cf 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
@@ -212,7 +212,7 @@ double CAESinkAUDIOTRACK::GetCacheTotal()
return m_sinkbuffer_sec + m_audiotrackbuffer_sec;
}
-unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
+unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
{
// write as many frames of audio as we can fit into our internal buffer.
@@ -243,6 +243,11 @@ unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t *data, unsigned int frames, b
break;
}
}
+ // AddPackets runs under a non-idled AE thread we must block or sleep.
+ // Trying to calc the optimal sleep is tricky so just a minimal sleep.
+ if(blocking)
+ Sleep(10);
+
return hasAudio ? write_frames:frames;
}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
index 3d73d9fb0b..9600867951 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
@@ -40,7 +40,7 @@ public:
virtual double GetDelay ();
virtual double GetCacheTime ();
virtual double GetCacheTotal ();
- virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
virtual void Drain ();
virtual bool HasVolume ();
virtual void SetVolume (float scale);
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp
index d46d551d5e..da98e1f78a 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp
@@ -35,6 +35,7 @@
#include <Functiondiscoverykeys_devpkey.h>
#include <Rpc.h>
#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "utils/StringUtils.h"
#pragma comment(lib, "Rpcrt4.lib")
extern HWND g_hWnd;
@@ -143,10 +144,14 @@ bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device)
LPGUID deviceGUID = NULL;
RPC_CSTR wszUuid = NULL;
HRESULT hr = E_FAIL;
+ std::string strDeviceGUID = device;
std::list<DSDevice> DSDeviceList;
std::string deviceFriendlyName;
DirectSoundEnumerate(DSEnumCallback, &DSDeviceList);
+ if(StringUtils::EndsWith(device, std::string("default")))
+ strDeviceGUID = GetDefaultDevice();
+
for (std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); ++itt)
{
if ((*itt).lpGuid)
@@ -154,23 +159,27 @@ bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device)
hr = (UuidToString((*itt).lpGuid, &wszUuid));
std::string sztmp = (char*)wszUuid;
std::string szGUID = "{" + std::string(sztmp.begin(), sztmp.end()) + "}";
- if (strcasecmp(szGUID.c_str(), device.c_str()) == 0)
+ if (strcasecmp(szGUID.c_str(), strDeviceGUID.c_str()) == 0)
{
deviceGUID = (*itt).lpGuid;
deviceFriendlyName = (*itt).name.c_str();
break;
}
}
- if (hr == RPC_S_OK) RpcStringFree(&wszUuid);
+ if (hr == RPC_S_OK) RpcStringFree(&wszUuid);
}
hr = DirectSoundCreate(deviceGUID, &m_pDSound, NULL);
if (FAILED(hr))
{
- CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device.");
- CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr));
- return false;
+ CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device %s with error %s, trying the default device.", deviceFriendlyName.c_str(), dserr2str(hr));
+ hr = DirectSoundCreate(NULL, &m_pDSound, NULL);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Failed to create the default DirectSound device with error %s.", dserr2str(hr));
+ return false;
+ }
}
HWND tmp_hWnd;
@@ -367,7 +376,7 @@ bool CAESinkDirectSound::IsCompatible(const AEAudioFormat format, const std::str
return false;
}
-unsigned int CAESinkDirectSound::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
+unsigned int CAESinkDirectSound::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
{
if (!m_initialized)
return 0;
@@ -386,10 +395,15 @@ unsigned int CAESinkDirectSound::AddPackets(uint8_t *data, unsigned int frames,
while (GetSpace() < total)
{
- if (m_isDirtyDS)
+ if(m_isDirtyDS)
return INT_MAX;
else
- return 0;
+ {
+ if(blocking)
+ Sleep(total * 1000 / m_AvgBytesPerSec);
+ else
+ return 0;
+ }
}
while (len)
@@ -398,7 +412,8 @@ unsigned int CAESinkDirectSound::AddPackets(uint8_t *data, unsigned int frames,
DWORD size = 0, sizeWrap = 0;
if (m_BufferOffset >= m_dwBufferLen) // Wrap-around manually
m_BufferOffset = 0;
- HRESULT res = m_pBuffer->Lock(m_BufferOffset, m_dwChunkSize, &start, &size, &startWrap, &sizeWrap, 0);
+ DWORD dwWriteBytes = std::min((int)m_dwChunkSize, (int)len);
+ HRESULT res = m_pBuffer->Lock(m_BufferOffset, dwWriteBytes, &start, &size, &startWrap, &sizeWrap, 0);
if (DS_OK != res)
{
CLog::Log(LOGERROR, __FUNCTION__ ": Unable to lock buffer at offset %u. HRESULT: 0x%08x", m_BufferOffset, res);
@@ -436,6 +451,23 @@ void CAESinkDirectSound::Stop()
m_pBuffer->Stop();
}
+void CAESinkDirectSound::Drain()
+{
+ if (!m_initialized || m_isDirtyDS)
+ return;
+
+ m_pBuffer->Stop();
+ HRESULT res = m_pBuffer->SetCurrentPosition(0);
+ if (DS_OK != res)
+ {
+ CLog::Log(LOGERROR,__FUNCTION__ ": SetCurrentPosition failed. Unable to determine buffer status. HRESULT = 0x%08x", res);
+ m_isDirtyDS = true;
+ return;
+ }
+ m_BufferOffset = 0;
+ UpdateCacheStatus();
+}
+
double CAESinkDirectSound::GetDelay()
{
if (!m_initialized)
@@ -478,6 +510,8 @@ void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bo
HRESULT hr;
+ std::string strDD = GetDefaultDevice();
+
/* See if we are on Windows XP */
if (!g_sysinfo.IsWindowsVersionAtLeast(CSysInfo::WindowsVersionVista))
{
@@ -512,6 +546,15 @@ void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bo
deviceInfo.m_sampleRates.push_back((DWORD) 96000);
deviceInfoList.push_back(deviceInfo);
+
+ // add the default device with m_deviceName = default
+ if(strDD == deviceInfo.m_deviceName)
+ {
+ deviceInfo.m_deviceName = std::string("default");
+ deviceInfo.m_displayName = std::string("default");
+ deviceInfo.m_displayNameExtra = std::string("");
+ deviceInfoList.push_back(deviceInfo);
+ }
}
RpcStringFree(&cszGUID);
@@ -627,22 +670,14 @@ void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bo
deviceInfo.m_deviceType = aeDeviceType;
deviceInfoList.push_back(deviceInfo);
- }
- // since AE takes the first device in deviceInfoList as default audio device we need
- // to sort it in order to use the real default device
- if(deviceInfoList.size() > 1)
- {
- std::string strDD = GetDefaultDevice();
- for (AEDeviceInfoList::iterator itt = deviceInfoList.begin(); itt != deviceInfoList.end(); ++itt)
+ // add the default device with m_deviceName = default
+ if(strDD == strDevName)
{
- CAEDeviceInfo devInfo = *itt;
- if(devInfo.m_deviceName == strDD)
- {
- deviceInfoList.erase(itt);
- deviceInfoList.insert(deviceInfoList.begin(), devInfo);
- break;
- }
+ deviceInfo.m_deviceName = std::string("default");
+ deviceInfo.m_displayName = std::string("default");
+ deviceInfo.m_displayNameExtra = std::string("");
+ deviceInfoList.push_back(deviceInfo);
}
}
@@ -677,10 +712,6 @@ void CAESinkDirectSound::CheckPlayStatus()
bool CAESinkDirectSound::UpdateCacheStatus()
{
CSingleLock lock (m_runLock);
- // TODO: Check to see if we may have cycled around since last time
- unsigned int time = XbmcThreads::SystemClockMillis();
- if (time == m_LastCacheCheck)
- return true; // Don't recalc more frequently than once/ms (that is our max resolution anyway)
DWORD playCursor = 0, writeCursor = 0;
HRESULT res = m_pBuffer->GetCurrentPosition(&playCursor, &writeCursor); // Get the current playback and safe write positions
@@ -691,7 +722,6 @@ bool CAESinkDirectSound::UpdateCacheStatus()
return false;
}
- m_LastCacheCheck = time;
// Check the state of the ring buffer (P->O->W == underrun)
// These are the logical situations that can occur
// O: CurrentOffset W: WriteCursor P: PlayCursor
@@ -723,7 +753,8 @@ bool CAESinkDirectSound::UpdateCacheStatus()
return false;
}
}
- else m_BufferTimeouts = 0;
+ else
+ m_BufferTimeouts = 0;
// Calculate available space in the ring buffer
if (playCursor == m_BufferOffset && m_BufferOffset == writeCursor) // Playback is stopped and we are all at the same place
@@ -910,4 +941,4 @@ bool CAESinkDirectSound::SoftResume()
{
/* Return false to force re-init by engine */
return false;
-} \ No newline at end of file
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h
index b0149dd13b..a6f2912a33 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h
@@ -39,10 +39,11 @@ public:
virtual bool IsCompatible(const AEAudioFormat format, const std::string &device);
virtual void Stop ();
+ virtual void Drain ();
virtual double GetDelay ();
virtual double GetCacheTime ();
virtual double GetCacheTotal ();
- virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
virtual bool SoftSuspend ();
virtual bool SoftResume ();
static std::string GetDefaultDevice ();
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp
index 254b9c9d98..7d26017b75 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp
@@ -99,7 +99,7 @@ double CAESinkNULL::GetCacheTotal()
return m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_size;
}
-unsigned int CAESinkNULL::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
+unsigned int CAESinkNULL::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
{
unsigned int max_frames = (m_sinkbuffer_size - m_sinkbuffer_level) / m_sink_frameSize;
if (frames > max_frames)
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h
index ef771196f1..1e4a3b9f49 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h
@@ -38,7 +38,7 @@ public:
virtual double GetDelay ();
virtual double GetCacheTime ();
virtual double GetCacheTotal ();
- virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
virtual void Drain ();
static void EnumerateDevices(AEDeviceList &devices, bool passthrough);
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp
index 281088dc57..b201b7b0bb 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp
@@ -408,7 +408,7 @@ double CAESinkOSS::GetDelay()
return (double)delay / (m_format.m_frameSize * m_format.m_sampleRate);
}
-unsigned int CAESinkOSS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
+unsigned int CAESinkOSS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
{
int size = frames * m_format.m_frameSize;
if (m_fd == -1)
@@ -420,7 +420,7 @@ unsigned int CAESinkOSS::AddPackets(uint8_t *data, unsigned int frames, bool has
int wrote = write(m_fd, data, size);
if (wrote < 0)
{
- if(errno == EAGAIN || errno == EWOULDBLOCK)
+ if(!blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
return 0;
CLog::Log(LOGERROR, "CAESinkOSS::AddPackets - Failed to write");
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h
index f50f713cda..9e558d3881 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h
@@ -41,7 +41,7 @@ public:
virtual double GetDelay ();
virtual double GetCacheTime () { return 0.0; } /* FIXME */
virtual double GetCacheTotal () { return 0.0; } /* FIXME */
- virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
virtual void Drain ();
static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
private:
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp
index 2cff0ed20e..aeceb6b554 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp
@@ -71,7 +71,7 @@ double CAESinkProfiler::GetDelay()
return 0.0f;
}
-unsigned int CAESinkProfiler::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
+unsigned int CAESinkProfiler::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
{
int64_t ts = CurrentHostCounter();
CLog::Log(LOGDEBUG, "CAESinkProfiler::AddPackets - latency %f ms", (float)(ts - m_ts) / 1000000.0f);
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h
index d9423fb612..a0b955b3e6 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h
@@ -39,7 +39,7 @@ public:
virtual double GetDelay ();
virtual double GetCacheTime () { return 0.0; }
virtual double GetCacheTotal () { return 0.0; }
- virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
virtual void Drain ();
static void EnumerateDevices(AEDeviceList &devices, bool passthrough);
private:
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp
index 9af3f2e52e..38065cd16a 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp
@@ -34,6 +34,7 @@
#include "../Utils/AEDeviceInfo.h"
#include <Mmreg.h>
#include <mmdeviceapi.h>
+#include "utils/StringUtils.h"
#pragma comment(lib, "Avrt.lib")
@@ -190,7 +191,10 @@ CAESinkWASAPI::CAESinkWASAPI() :
m_isDirty(false),
m_uiBufferLen(0),
m_avgTimeWaiting(50),
- m_sinkLatency(0.0)
+ m_sinkLatency(0.0),
+ m_pBuffer(NULL),
+ m_bufferPtr(0),
+ m_hnsRequestedDuration(0)
{
m_channelLayout.Reset();
}
@@ -206,6 +210,7 @@ bool CAESinkWASAPI::Initialize(AEAudioFormat &format, std::string &device)
return false;
m_device = device;
+ bool bdefault = false;
/* Save requested format */
/* Clear returned format */
@@ -227,41 +232,47 @@ bool CAESinkWASAPI::Initialize(AEAudioFormat &format, std::string &device)
hr = pEnumDevices->GetCount(&uiCount);
EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
- for (UINT i = 0; i < uiCount; i++)
+ if(StringUtils::EndsWith(device, std::string("default")))
+ bdefault = true;
+
+ if(!bdefault)
{
- IPropertyStore *pProperty = NULL;
- PROPVARIANT varName;
+ for (UINT i = 0; i < uiCount; i++)
+ {
+ IPropertyStore *pProperty = NULL;
+ PROPVARIANT varName;
- hr = pEnumDevices->Item(i, &m_pDevice);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint failed.")
+ hr = pEnumDevices->Item(i, &m_pDevice);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint failed.")
- hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.")
+ hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.")
- hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
- if (FAILED(hr))
- {
- CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint GUID failed.");
- SAFE_RELEASE(pProperty);
- goto failed;
- }
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint GUID failed.");
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
- std::string strDevName = localWideToUtf(varName.pwszVal);
+ std::string strDevName = localWideToUtf(varName.pwszVal);
- if (device == strDevName)
- i = uiCount;
- else
- SAFE_RELEASE(m_pDevice);
+ if (device == strDevName)
+ i = uiCount;
+ else
+ SAFE_RELEASE(m_pDevice);
- PropVariantClear(&varName);
- SAFE_RELEASE(pProperty);
+ PropVariantClear(&varName);
+ SAFE_RELEASE(pProperty);
+ }
}
-
SAFE_RELEASE(pEnumDevices);
if (!m_pDevice)
{
- CLog::Log(LOGINFO, __FUNCTION__": Could not locate the device named \"%s\" in the list of WASAPI endpoint devices. Trying the default device...", device.c_str());
+ if(!bdefault)
+ CLog::Log(LOGINFO, __FUNCTION__": Could not locate the device named \"%s\" in the list of WASAPI endpoint devices. Trying the default device...", device.c_str());
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_pDevice);
EXIT_ON_FAILURE(hr, __FUNCTION__": Could not retrieve the default WASAPI audio endpoint.")
@@ -307,6 +318,14 @@ bool CAESinkWASAPI::Initialize(AEAudioFormat &format, std::string &device)
m_initialized = true;
m_isDirty = false;
+ // allow feeding less samples than buffer size
+ // if the device is opened exclusive and event driven, provided samples must match buffersize
+ // ActiveAE tries to align provided samples with buffer size but cannot guarantee (e.g. transcoding)
+ // this can be avoided by dropping the event mode which has not much benefit; SoftAE polls anyway
+ delete [] m_pBuffer;
+ m_pBuffer = new uint8_t[format.m_frames * format.m_frameSize];
+ m_bufferPtr = 0;
+
return true;
failed:
@@ -351,6 +370,9 @@ void CAESinkWASAPI::Deinitialize()
SAFE_RELEASE(m_pDevice);
m_initialized = false;
+
+ delete [] m_pBuffer;
+ m_bufferPtr = 0;
}
bool CAESinkWASAPI::IsCompatible(const AEAudioFormat format, const std::string &device)
@@ -423,7 +445,7 @@ double CAESinkWASAPI::GetCacheTotal()
return m_sinkLatency;
}
-unsigned int CAESinkWASAPI::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
+unsigned int CAESinkWASAPI::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
{
if (!m_initialized)
return 0;
@@ -438,7 +460,15 @@ unsigned int CAESinkWASAPI::AddPackets(uint8_t *data, unsigned int frames, bool
LARGE_INTEGER timerFreq;
#endif
- unsigned int NumFramesRequested = frames;
+ unsigned int NumFramesRequested = m_format.m_frames;
+ unsigned int FramesToCopy = std::min(m_format.m_frames - m_bufferPtr, frames);
+ if (m_bufferPtr != 0 || frames != m_format.m_frames)
+ {
+ memcpy(m_pBuffer+m_bufferPtr*m_format.m_frameSize, data, FramesToCopy*m_format.m_frameSize);
+ m_bufferPtr += FramesToCopy;
+ if (frames != m_format.m_frames)
+ return frames;
+ }
if (!m_running) //first time called, pre-fill buffer then start audio client
{
@@ -488,10 +518,33 @@ unsigned int CAESinkWASAPI::AddPackets(uint8_t *data, unsigned int frames, bool
#endif
/* Wait for Audio Driver to tell us it's got a buffer available */
- DWORD eventAudioCallback = WaitForSingleObject(m_needDataEvent, 0);
+ DWORD eventAudioCallback;
+ if(!blocking)
+ eventAudioCallback = WaitForSingleObject(m_needDataEvent, 0);
+ else
+ eventAudioCallback = WaitForSingleObject(m_needDataEvent, 1100);
+
+ if (!blocking)
+ {
+ if(eventAudioCallback != WAIT_OBJECT_0)
+ return 0;
+ }
+ else
+ {
+ if(eventAudioCallback != WAIT_OBJECT_0 || !&buf)
+ {
+ /* Event handle timed out - flag sink as dirty for re-initializing */
+ CLog::Log(LOGERROR, __FUNCTION__": Endpoint Buffer timed out");
+ if (g_advancedSettings.m_streamSilence)
+ {
+ m_isDirty = true; //flag new device or re-init needed
+ Deinitialize();
+ m_running = false;
+ return INT_MAX;
+ }
+ }
+ }
- if (eventAudioCallback != WAIT_OBJECT_0)
- return 0;
if (!m_running)
return 0;
@@ -516,7 +569,8 @@ unsigned int CAESinkWASAPI::AddPackets(uint8_t *data, unsigned int frames, bool
#endif
return INT_MAX;
}
- memcpy(buf, data, NumFramesRequested * m_format.m_frameSize); //fill buffer
+ memcpy(buf, m_bufferPtr == 0 ? data : m_pBuffer, NumFramesRequested * m_format.m_frameSize); //fill buffer
+ m_bufferPtr = 0;
hr = m_pRenderClient->ReleaseBuffer(NumFramesRequested, flags); //pass back to audio driver
if (FAILED(hr))
{
@@ -526,7 +580,13 @@ unsigned int CAESinkWASAPI::AddPackets(uint8_t *data, unsigned int frames, bool
return INT_MAX;
}
- return NumFramesRequested;
+ if (FramesToCopy != frames)
+ {
+ m_bufferPtr = frames-FramesToCopy;
+ memcpy(m_pBuffer, data+FramesToCopy*m_format.m_frameSize, m_bufferPtr*m_format.m_frameSize);
+ }
+
+ return frames;
}
bool CAESinkWASAPI::SoftSuspend()
@@ -554,8 +614,11 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
{
IMMDeviceEnumerator* pEnumerator = NULL;
IMMDeviceCollection* pEnumDevices = NULL;
+ IMMDevice* pDefaultDevice = NULL;
CAEDeviceInfo deviceInfo;
CAEChannelInfo deviceChannels;
+ LPWSTR pwszID = NULL;
+ std::wstring wstrDDID;
WAVEFORMATEXTENSIBLE wfxex = {0};
HRESULT hr;
@@ -565,6 +628,18 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
UINT uiCount = 0;
+ // get the default audio endpoint
+ if(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDefaultDevice) == S_OK)
+ {
+ if(pDefaultDevice->GetId(&pwszID) == S_OK)
+ {
+ wstrDDID = pwszID;
+ CoTaskMemFree(pwszID);
+ }
+ SAFE_RELEASE(pDefaultDevice);
+ }
+
+ // enumerate over all audio endpoints
hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
@@ -836,9 +911,6 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
CLog::Log(LOGDEBUG, __FUNCTION__": Failed to activate device for passthrough capability testing.");
}
- SAFE_RELEASE(pDevice);
- SAFE_RELEASE(pProperty);
-
deviceInfo.m_deviceName = strDevName;
deviceInfo.m_displayName = strWinDevType.append(strFriendlyName);
deviceInfo.m_displayNameExtra = std::string("WASAPI: ").append(strFriendlyName);
@@ -847,6 +919,21 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
/* Store the device info */
deviceInfoList.push_back(deviceInfo);
+
+ if(pDevice->GetId(&pwszID) == S_OK)
+ {
+ if(wstrDDID.compare(pwszID) == 0)
+ {
+ deviceInfo.m_deviceName = std::string("default");
+ deviceInfo.m_displayName = std::string("default");
+ deviceInfo.m_displayNameExtra = std::string("");
+ deviceInfoList.push_back(deviceInfo);
+ }
+ CoTaskMemFree(pwszID);
+ }
+
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
}
return;
@@ -1095,6 +1182,8 @@ initialize:
hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, &wfxex.Format, NULL);
+ m_hnsRequestedDuration = audioSinkBufferDurationMsec;
+
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
{
/* WASAPI requires aligned buffer */
@@ -1224,3 +1313,25 @@ const char *CAESinkWASAPI::WASAPIErrToStr(HRESULT err)
}
return NULL;
}
+
+void CAESinkWASAPI::Drain()
+{
+ if(!m_pAudioClient)
+ return;
+
+ Sleep( (DWORD)(m_hnsRequestedDuration / 10000));
+
+ if (m_running)
+ {
+ try
+ {
+ m_pAudioClient->Stop(); //stop the audio output
+ m_pAudioClient->Reset(); //flush buffer and reset audio clock stream position
+ }
+ catch (...)
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__, "Invalidated AudioClient - Releasing");
+ }
+ }
+ m_running = false;
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h
index df16682a40..39d62d534b 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h
@@ -42,9 +42,10 @@ public:
virtual double GetDelay ();
virtual double GetCacheTime ();
virtual double GetCacheTotal ();
- virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
virtual bool SoftSuspend ();
virtual bool SoftResume ();
+ virtual void Drain ();
static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList, bool force = false);
private:
bool InitializeExclusive(AEAudioFormat &format);
@@ -78,4 +79,8 @@ private:
unsigned int m_uiBufferLen; /* wasapi endpoint buffer size, in frames */
double m_avgTimeWaiting; /* time between next buffer of data from SoftAE and driver call for data */
double m_sinkLatency; /* time in seconds of total duration of the two WASAPI buffers */
+
+ uint8_t *m_pBuffer;
+ int m_bufferPtr;
+ REFERENCE_TIME m_hnsRequestedDuration;
};
diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.cpp b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
index 2b6e0cd5bb..3a5c78f443 100644
--- a/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
+++ b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
@@ -82,26 +82,33 @@ const unsigned int CAEUtil::DataFormatToBits(const enum AEDataFormat dataFormat)
static const unsigned int formats[AE_FMT_MAX] =
{
8, /* U8 */
+ 8, /* U8P */
8, /* S8 */
16, /* S16BE */
16, /* S16LE */
16, /* S16NE */
+ 16, /* S16NEP */
32, /* S32BE */
32, /* S32LE */
32, /* S32NE */
+ 32, /* S32NEP */
32, /* S24BE */
32, /* S24LE */
32, /* S24NE */
+ 32, /* S24NEP */
24, /* S24BE3 */
24, /* S24LE3 */
24, /* S24NE3 */
+ 24, /* S24NE3P*/
sizeof(double) << 3, /* DOUBLE */
+ sizeof(double) << 3, /* DOUBLEP */
sizeof(float ) << 3, /* FLOAT */
+ sizeof(float ) << 3, /* FLOATP */
16, /* AAC */
16, /* AC3 */
@@ -123,26 +130,33 @@ const char* CAEUtil::DataFormatToStr(const enum AEDataFormat dataFormat)
static const char *formats[AE_FMT_MAX] =
{
"AE_FMT_U8",
+ "AE_FMT_U8P",
"AE_FMT_S8",
"AE_FMT_S16BE",
"AE_FMT_S16LE",
"AE_FMT_S16NE",
+ "AE_FMT_S16NEP",
"AE_FMT_S32BE",
"AE_FMT_S32LE",
"AE_FMT_S32NE",
+ "AE_FMT_S32NEP",
"AE_FMT_S24BE4",
"AE_FMT_S24LE4",
"AE_FMT_S24NE4", /* S24 in 4 bytes */
+ "AE_FMT_S24NE4P",
"AE_FMT_S24BE3",
"AE_FMT_S24LE3",
"AE_FMT_S24NE3", /* S24 in 3 bytes */
+ "AE_FMT_S24NE3P",
"AE_FMT_DOUBLE",
+ "AE_FMT_DOUBLEP",
"AE_FMT_FLOAT",
+ "AE_FMT_FLOATP",
/* for passthrough streams and the like */
"AE_FMT_AAC",
diff --git a/xbmc/cores/dvdplayer/DVDAudio.cpp b/xbmc/cores/dvdplayer/DVDAudio.cpp
index c37d4c51ea..64044338ee 100644
--- a/xbmc/cores/dvdplayer/DVDAudio.cpp
+++ b/xbmc/cores/dvdplayer/DVDAudio.cpp
@@ -298,7 +298,7 @@ void CDVDAudio::Drain()
Finish();
CSingleLock lock (m_critSection);
if (m_pAudioStream)
- m_pAudioStream->Drain();
+ m_pAudioStream->Drain(true);
}
void CDVDAudio::RegisterAudioCallback(IAudioCallback* pCallback)
diff --git a/xbmc/cores/paplayer/PAPlayer.cpp b/xbmc/cores/paplayer/PAPlayer.cpp
index e4ba6100df..94a339397b 100644
--- a/xbmc/cores/paplayer/PAPlayer.cpp
+++ b/xbmc/cores/paplayer/PAPlayer.cpp
@@ -610,7 +610,7 @@ inline void PAPlayer::ProcessStreams(double &delay, double &buffer)
/* unregister the audio callback */
si->m_stream->UnRegisterAudioCallback();
si->m_decoder.Destroy();
- si->m_stream->Drain();
+ si->m_stream->Drain(false);
m_finishing.push_back(si);
return;
}
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index d1d5538a95..2a13509f44 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -362,6 +362,7 @@ void CSettings::Uninitialize()
// unregister setting option fillers
m_settingsManager->UnregisterSettingOptionsFiller("audiocdactions");
m_settingsManager->UnregisterSettingOptionsFiller("audiocdencoders");
+ m_settingsManager->UnregisterSettingOptionsFiller("aequalitylevels");
m_settingsManager->UnregisterSettingOptionsFiller("audiodevices");
m_settingsManager->UnregisterSettingOptionsFiller("audiodevicespassthrough");
m_settingsManager->UnregisterSettingOptionsFiller("audiooutputmodes");
@@ -645,7 +646,7 @@ void CSettings::InitializeDefaults()
((CSettingString*)m_settingsManager->GetSetting("audiooutput.audiodevice"))->SetDefault(defaultAudioDeviceName);
((CSettingString*)m_settingsManager->GetSetting("audiooutput.passthroughdevice"))->SetDefault(defaultAudioDeviceName);
#endif
-#else
+#elif !defined(TARGET_WINDOWS)
((CSettingString*)m_settingsManager->GetSetting("audiooutput.audiodevice"))->SetDefault(CAEFactory::GetDefaultDevice(false));
((CSettingString*)m_settingsManager->GetSetting("audiooutput.passthroughdevice"))->SetDefault(CAEFactory::GetDefaultDevice(true));
#endif
@@ -666,6 +667,7 @@ void CSettings::InitializeOptionFillers()
m_settingsManager->RegisterSettingOptionsFiller("audiocdactions", MEDIA_DETECT::CAutorun::SettingOptionAudioCdActionsFiller);
m_settingsManager->RegisterSettingOptionsFiller("audiocdencoders", MEDIA_DETECT::CAutorun::SettingOptionAudioCdEncodersFiller);
#endif
+ m_settingsManager->RegisterSettingOptionsFiller("aequalitylevels", CAEFactory::SettingOptionsAudioQualityLevelsFiller);
m_settingsManager->RegisterSettingOptionsFiller("audiodevices", CAEFactory::SettingOptionsAudioDevicesFiller);
m_settingsManager->RegisterSettingOptionsFiller("audiodevicespassthrough", CAEFactory::SettingOptionsAudioDevicesPassthroughFiller);
m_settingsManager->RegisterSettingOptionsFiller("audiooutputmodes", CAEFactory::SettingOptionsAudioOutputModesFiller);
@@ -766,6 +768,12 @@ void CSettings::InitializeConditions()
if (g_application.IsStandAlone())
m_settingsManager->AddCondition("isstandalone");
+ if (CAEFactory::SupportsDrain())
+ m_settingsManager->AddCondition("audiosupportsdrain");
+
+ if(CAEFactory::SupportsQualitySetting())
+ m_settingsManager->AddCondition("has_ae_quality_levels");
+
// add more complex conditions
m_settingsManager->AddCondition("addonhassettings", AddonHasSettings);
m_settingsManager->AddCondition("checkmasterlock", CheckMasterLock);
@@ -846,8 +854,20 @@ void CSettings::InitializeISettingCallbacks()
m_settingsManager->RegisterCallback(&CDisplaySettings::Get(), settingSet);
settingSet.clear();
+ settingSet.insert("audiooutput.mode");
settingSet.insert("audiooutput.channels");
+ settingSet.insert("audiooutput.processquality");
settingSet.insert("audiooutput.guisoundmode");
+ settingSet.insert("audiooutput.stereoupmix");
+ settingSet.insert("audiooutput.ac3passthrough");
+ settingSet.insert("audiooutput.dtspassthrough");
+ settingSet.insert("audiooutput.passthroughaac");
+ settingSet.insert("audiooutput.truehdpassthrough");
+ settingSet.insert("audiooutput.dtshdpassthrough");
+ settingSet.insert("audiooutput.multichannellpcm");
+ settingSet.insert("audiooutput.audiodevice");
+ settingSet.insert("audiooutput.passthroughdevice");
+ settingSet.insert("audiooutput.streamsilence");
settingSet.insert("lookandfeel.skin");
settingSet.insert("lookandfeel.skinsettings");
settingSet.insert("lookandfeel.font");
diff --git a/xbmc/utils/ActorProtocol.cpp b/xbmc/utils/ActorProtocol.cpp
new file mode 100644
index 0000000000..cf4c26f21d
--- /dev/null
+++ b/xbmc/utils/ActorProtocol.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ActorProtocol.h"
+
+using namespace Actor;
+
+void Message::Release()
+{
+ bool skip;
+ origin->Lock();
+ skip = isSync ? !isSyncFini : false;
+ isSyncFini = true;
+ origin->Unlock();
+
+ if (skip)
+ return;
+
+ // free data buffer
+ if (data != buffer)
+ delete [] data;
+
+ // delete event in case of sync message
+ if (event)
+ delete event;
+
+ origin->ReturnMessage(this);
+}
+
+bool Message::Reply(int sig, void *data /* = NULL*/, int size /* = 0 */)
+{
+ if (!isSync)
+ {
+ if (isOut)
+ return origin->SendInMessage(sig, data, size);
+ else
+ return origin->SendOutMessage(sig, data, size);
+ }
+
+ origin->Lock();
+
+ if (!isSyncTimeout)
+ {
+ Message *msg = origin->GetMessage();
+ msg->signal = sig;
+ msg->isOut = !isOut;
+ replyMessage = msg;
+ if (data)
+ {
+ if (size > MSG_INTERNAL_BUFFER_SIZE)
+ msg->data = new uint8_t[size];
+ else
+ msg->data = msg->buffer;
+ memcpy(msg->data, data, size);
+ }
+ }
+
+ origin->Unlock();
+
+ if (event)
+ event->Set();
+
+ return true;
+}
+
+Protocol::~Protocol()
+{
+ Message *msg;
+ Purge();
+ while (!freeMessageQueue.empty())
+ {
+ msg = freeMessageQueue.front();
+ freeMessageQueue.pop();
+ delete msg;
+ }
+}
+
+Message *Protocol::GetMessage()
+{
+ Message *msg;
+
+ CSingleLock lock(criticalSection);
+
+ if (!freeMessageQueue.empty())
+ {
+ msg = freeMessageQueue.front();
+ freeMessageQueue.pop();
+ }
+ else
+ msg = new Message();
+
+ msg->isSync = false;
+ msg->isSyncFini = false;
+ msg->isSyncTimeout = false;
+ msg->event = NULL;
+ msg->data = NULL;
+ msg->payloadSize = 0;
+ msg->replyMessage = NULL;
+ msg->origin = this;
+
+ return msg;
+}
+
+void Protocol::ReturnMessage(Message *msg)
+{
+ CSingleLock lock(criticalSection);
+
+ freeMessageQueue.push(msg);
+}
+
+bool Protocol::SendOutMessage(int signal, void *data /* = NULL */, int size /* = 0 */, Message *outMsg /* = NULL */)
+{
+ Message *msg;
+ if (outMsg)
+ msg = outMsg;
+ else
+ msg = GetMessage();
+
+ msg->signal = signal;
+ msg->isOut = true;
+
+ if (data)
+ {
+ if (size > MSG_INTERNAL_BUFFER_SIZE)
+ msg->data = new uint8_t[size];
+ else
+ msg->data = msg->buffer;
+ memcpy(msg->data, data, size);
+ }
+
+ { CSingleLock lock(criticalSection);
+ outMessages.push(msg);
+ }
+ containerOutEvent->Set();
+
+ return true;
+}
+
+bool Protocol::SendInMessage(int signal, void *data /* = NULL */, int size /* = 0 */, Message *outMsg /* = NULL */)
+{
+ Message *msg;
+ if (outMsg)
+ msg = outMsg;
+ else
+ msg = GetMessage();
+
+ msg->signal = signal;
+ msg->isOut = false;
+
+ if (data)
+ {
+ if (size > MSG_INTERNAL_BUFFER_SIZE)
+ msg->data = new uint8_t[size];
+ else
+ msg->data = msg->buffer;
+ memcpy(msg->data, data, size);
+ }
+
+ { CSingleLock lock(criticalSection);
+ inMessages.push(msg);
+ }
+ containerInEvent->Set();
+
+ return true;
+}
+
+
+bool Protocol::SendOutMessageSync(int signal, Message **retMsg, int timeout, void *data /* = NULL */, int size /* = 0 */)
+{
+ Message *msg = GetMessage();
+ msg->isOut = true;
+ msg->isSync = true;
+ msg->event = new CEvent;
+ msg->event->Reset();
+ SendOutMessage(signal, data, size, msg);
+
+ if (!msg->event->WaitMSec(timeout))
+ {
+ msg->origin->Lock();
+ if (msg->replyMessage)
+ *retMsg = msg->replyMessage;
+ else
+ {
+ *retMsg = NULL;
+ msg->isSyncTimeout = true;
+ }
+ msg->origin->Unlock();
+ }
+ else
+ *retMsg = msg->replyMessage;
+
+ msg->Release();
+
+ if (*retMsg)
+ return true;
+ else
+ return false;
+}
+
+bool Protocol::ReceiveOutMessage(Message **msg)
+{
+ CSingleLock lock(criticalSection);
+
+ if (outMessages.empty() || outDefered)
+ return false;
+
+ *msg = outMessages.front();
+ outMessages.pop();
+
+ return true;
+}
+
+bool Protocol::ReceiveInMessage(Message **msg)
+{
+ CSingleLock lock(criticalSection);
+
+ if (inMessages.empty() || inDefered)
+ return false;
+
+ *msg = inMessages.front();
+ inMessages.pop();
+
+ return true;
+}
+
+
+void Protocol::Purge()
+{
+ Message *msg;
+
+ while (ReceiveInMessage(&msg))
+ msg->Release();
+
+ while (ReceiveOutMessage(&msg))
+ msg->Release();
+}
diff --git a/xbmc/utils/ActorProtocol.h b/xbmc/utils/ActorProtocol.h
new file mode 100644
index 0000000000..8ef3359698
--- /dev/null
+++ b/xbmc/utils/ActorProtocol.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include "threads/Thread.h"
+#include "utils/log.h"
+#include <queue>
+#include "memory.h"
+
+#define MSG_INTERNAL_BUFFER_SIZE 32
+
+namespace Actor
+{
+
+class Protocol;
+
+class Message
+{
+ friend class Protocol;
+public:
+ int signal;
+ bool isSync;
+ bool isSyncFini;
+ bool isOut;
+ bool isSyncTimeout;
+ int payloadSize;
+ uint8_t buffer[MSG_INTERNAL_BUFFER_SIZE];
+ uint8_t *data;
+ Message *replyMessage;
+ Protocol *origin;
+ CEvent *event;
+
+ void Release();
+ bool Reply(int sig, void *data = NULL, int size = 0);
+
+private:
+ Message() {isSync = false; data = NULL; event = NULL; replyMessage = NULL;};
+};
+
+class Protocol
+{
+public:
+ Protocol(std::string name, CEvent* inEvent, CEvent *outEvent)
+ : portName(name), inDefered(false), outDefered(false) {containerInEvent = inEvent; containerOutEvent = outEvent;};
+ virtual ~Protocol();
+ Message *GetMessage();
+ void ReturnMessage(Message *msg);
+ bool SendOutMessage(int signal, void *data = NULL, int size = 0, Message *outMsg = NULL);
+ bool SendInMessage(int signal, void *data = NULL, int size = 0, Message *outMsg = NULL);
+ bool SendOutMessageSync(int signal, Message **retMsg, int timeout, void *data = NULL, int size = 0);
+ bool ReceiveOutMessage(Message **msg);
+ bool ReceiveInMessage(Message **msg);
+ void Purge();
+ void DeferIn(bool value) {inDefered = value;};
+ void DeferOut(bool value) {outDefered = value;};
+ void Lock() {criticalSection.lock();};
+ void Unlock() {criticalSection.unlock();};
+ std::string portName;
+
+protected:
+ CEvent *containerInEvent, *containerOutEvent;
+ CCriticalSection criticalSection;
+ std::queue<Message*> outMessages;
+ std::queue<Message*> inMessages;
+ std::queue<Message*> freeMessageQueue;
+ bool inDefered, outDefered;
+};
+
+}
diff --git a/xbmc/utils/Makefile.in b/xbmc/utils/Makefile.in
index 694a9bde84..ca49be25a3 100644
--- a/xbmc/utils/Makefile.in
+++ b/xbmc/utils/Makefile.in
@@ -70,6 +70,7 @@ SRCS += Vector.cpp
SRCS += Weather.cpp
SRCS += XBMCTinyXML.cpp
SRCS += XMLUtils.cpp
+SRCS += ActorProtocol.cpp
ifeq (@USE_OPENGLES@,1)
SRCS += AMLUtils.cpp