]> git.sesse.net Git - casparcg/commitdiff
2.0.0.2: Refactored mixer and fixed some mixer protocol bugs.
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sun, 10 Apr 2011 12:24:28 +0000 (12:24 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sun, 10 Apr 2011 12:24:28 +0000 (12:24 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.0.0.2@629 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

core/producer/frame_producer_device.cpp
mixer/frame_mixer_device.cpp
mixer/frame_mixer_device.h
protocol/amcp/AMCPCommandsImpl.cpp

index 37b6e609702c136ebeac80cae4734cad70414ab2..2ebb1756111eb18575e602fe45bb3b8dd355f554 100644 (file)
@@ -83,8 +83,6 @@ public:
                                frames[i]->set_layer_index(it->first);\r
                        }\r
                });             \r
-               boost::range::remove_erase(frames, basic_frame::empty());\r
-               boost::range::remove_erase(frames, basic_frame::eof());\r
                return frames;\r
        }\r
 \r
index f7f5a7f813ac4da259157c4d1066d5bcb4410ecd..d973ad630e583d00b4a50291420f9466671e475c 100644 (file)
@@ -5,9 +5,6 @@
 #include "gpu/gpu_read_frame.h"\r
 #include "gpu/gpu_write_frame.h"\r
 \r
-#include <core/producer/frame/audio_transform.h>\r
-#include <core/producer/frame/image_transform.h>\r
-\r
 #include "audio/audio_mixer.h"\r
 #include "image/image_mixer.h"\r
 \r
 #include <common/utility/timer.h>\r
 #include <common/utility/tweener.h>\r
 \r
+#include <core/producer/frame/audio_transform.h>\r
+#include <core/producer/frame/image_transform.h>\r
+\r
 #include <core/video_format.h>\r
 \r
+#include <boost/fusion/container/map.hpp>\r
+#include <boost/fusion/include/at_key.hpp>\r
+\r
 #include <unordered_map>\r
 \r
 namespace caspar { namespace mixer {\r
@@ -44,11 +47,12 @@ public:
                , time_(0)\r
                , tweener_(get_tweener(tween)){}\r
        \r
-       virtual T fetch()\r
+       T fetch()\r
        {\r
-               return tween(static_cast<double>(time_), source_, dest_, static_cast<double>(duration_)+0.000001, tweener_);\r
+               return time_ == duration_ ? dest_ : tween(static_cast<double>(time_), source_, dest_, static_cast<double>(duration_), tweener_);\r
        }\r
-       virtual T fetch_and_tick(int num)\r
+\r
+       T fetch_and_tick(int num)\r
        {                                               \r
                time_ = std::min(time_+num, duration_);\r
                return fetch();\r
@@ -67,12 +71,15 @@ struct frame_mixer_device::implementation : boost::noncopyable
        image_mixer image_mixer_;\r
 \r
        output_t output_;\r
+       \r
+       typedef std::unordered_map<int, tweened_transform<core::image_transform>> image_transforms;\r
+       typedef std::unordered_map<int, tweened_transform<core::audio_transform>> audio_transforms;\r
 \r
-       std::unordered_map<int, tweened_transform<core::image_transform>> image_transforms_;\r
-       std::unordered_map<int, tweened_transform<core::audio_transform>> audio_transforms_;\r
-\r
-       tweened_transform<core::image_transform> root_image_transform_;\r
-       tweened_transform<core::audio_transform> root_audio_transform_;\r
+       boost::fusion::map<boost::fusion::pair<core::image_transform, image_transforms>,\r
+                                       boost::fusion::pair<core::audio_transform, audio_transforms>> transforms_;\r
+       \r
+       boost::fusion::map<boost::fusion::pair<core::image_transform, tweened_transform<core::image_transform>>,\r
+                                       boost::fusion::pair<core::audio_transform, tweened_transform<core::audio_transform>>> root_transforms_;\r
 \r
        executor executor_;\r
 public:\r
@@ -80,7 +87,7 @@ public:
                : format_desc_(format_desc)\r
                , diag_(diagnostics::create_graph(narrow(print())))\r
                , image_mixer_(format_desc)\r
-               , executor_(print())\r
+               , executor_(L"frame_mixer_device")\r
        {\r
                diag_->add_guide("frame-time", 0.5f);   \r
                diag_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
@@ -100,6 +107,9 @@ public:
        {\r
                frames.erase(std::remove(frames.begin(), frames.end(), core::basic_frame::empty()), frames.end());\r
                frames.erase(std::remove(frames.begin(), frames.end(), core::basic_frame::eof()), frames.end());\r
+               \r
+               auto& root_image_transform = boost::fusion::at_key<core::image_transform>(root_transforms_);\r
+               auto& image_transforms = boost::fusion::at_key<core::image_transform>(transforms_);\r
 \r
                auto image = image_mixer_.begin_pass();\r
                BOOST_FOREACH(auto& frame, frames)\r
@@ -108,9 +118,9 @@ public:
                        {\r
                                auto frame1 = make_safe<core::basic_frame>(frame);\r
                                auto frame2 = make_safe<core::basic_frame>(frame);\r
-\r
-                               frame1->get_image_transform() = root_image_transform_.fetch_and_tick(1)*image_transforms_[frame->get_layer_index()].fetch_and_tick(1);\r
-                               frame2->get_image_transform() = root_image_transform_.fetch_and_tick(1)*image_transforms_[frame->get_layer_index()].fetch_and_tick(1);\r
+                               \r
+                               frame1->get_image_transform() = root_image_transform.fetch_and_tick(1)*image_transforms[frame->get_layer_index()].fetch_and_tick(1);\r
+                               frame2->get_image_transform() = root_image_transform.fetch_and_tick(1)*image_transforms[frame->get_layer_index()].fetch_and_tick(1);\r
 \r
                                if(frame1->get_image_transform() != frame2->get_image_transform())\r
                                        core::basic_frame::interlace(frame1, frame2, format_desc_.mode)->accept(image_mixer_);\r
@@ -120,7 +130,7 @@ public:
                        else\r
                        {\r
                                auto frame1 = make_safe<core::basic_frame>(frame);\r
-                               frame1->get_image_transform() = root_image_transform_.fetch_and_tick(1)*image_transforms_[frame->get_layer_index()].fetch_and_tick(1);\r
+                               frame1->get_image_transform() = root_image_transform.fetch_and_tick(1)*image_transforms[frame->get_layer_index()].fetch_and_tick(1);\r
                                frame1->accept(image_mixer_);\r
                        }\r
                }\r
@@ -130,13 +140,16 @@ public:
 \r
        std::vector<short> mix_audio(const std::vector<safe_ptr<core::basic_frame>>& frames)\r
        {\r
+               auto& root_audio_transform = boost::fusion::at_key<core::audio_transform>(root_transforms_);\r
+               auto& audio_transforms = boost::fusion::at_key<core::audio_transform>(transforms_);\r
+\r
                auto audio = audio_mixer_.begin_pass();\r
                BOOST_FOREACH(auto& frame, frames)\r
                {\r
                        int num = format_desc_.mode == core::video_mode::progressive ? 1 : 2;\r
 \r
                        auto frame1 = make_safe<core::basic_frame>(frame);\r
-                       frame1->get_audio_transform() = root_audio_transform_.fetch_and_tick(num)*audio_transforms_[frame->get_layer_index()].fetch_and_tick(num);\r
+                       frame1->get_audio_transform() = root_audio_transform.fetch_and_tick(num)*audio_transforms[frame->get_layer_index()].fetch_and_tick(num);\r
                        frame1->accept(audio_mixer_);\r
                }\r
                audio_mixer_.end_pass();\r
@@ -169,107 +182,81 @@ public:
        {\r
                return make_safe<gpu_write_frame>(reinterpret_cast<int>(tag), desc, image_mixer_.create_buffers(desc));\r
        }\r
-                               \r
-       void set_image_transform(const core::image_transform& transform, int mix_duration, const std::wstring& tween)\r
+                       \r
+       template<typename T>    \r
+       void set_transform(const T& transform, int mix_duration, const std::wstring& tween)\r
        {\r
                executor_.invoke([&]\r
                {\r
-                       auto src = root_image_transform_.fetch();\r
-                       auto dst = transform;\r
-                       root_image_transform_ = tweened_transform<core::image_transform>(src, dst, mix_duration, tween);\r
-               });\r
-       }\r
+                       auto& root = boost::fusion::at_key<T>(root_transforms_);\r
 \r
-       void set_audio_transform(const core::audio_transform& transform, int mix_duration, const std::wstring& tween)\r
-       {\r
-               executor_.invoke([&]\r
-               {\r
-                       auto src = root_audio_transform_.fetch();\r
+                       auto src = root.fetch();\r
                        auto dst = transform;\r
-                       root_audio_transform_ = tweened_transform<core::audio_transform>(src, dst, mix_duration, tween);\r
+                       root = tweened_transform<T>(src, dst, mix_duration, tween);\r
                });\r
        }\r
-\r
-       void set_image_transform(int index, const core::image_transform& transform, int mix_duration, const std::wstring& tween)\r
+               \r
+       template<typename T>\r
+       void set_transform(int index, const T& transform, int mix_duration, const std::wstring& tween)\r
        {\r
                executor_.invoke([&]\r
                {\r
-                       auto src = image_transforms_[index].fetch();\r
-                       auto dst = transform;\r
-                       image_transforms_[index] = tweened_transform<core::image_transform>(src, dst, mix_duration, tween);\r
-               });\r
-       }\r
+                       auto& transforms = boost::fusion::at_key<T>(transforms_);\r
 \r
-       void set_audio_transform(int index, const core::audio_transform& transform, int mix_duration, const std::wstring& tween)\r
-       {\r
-               executor_.invoke([&]\r
-               {\r
-                       auto src = audio_transforms_[index].fetch();\r
+                       auto src = transforms[index].fetch();\r
                        auto dst = transform;\r
-                       audio_transforms_[index] = tweened_transform<core::audio_transform>(src, dst, mix_duration, tween);\r
+                       transforms[index] = tweened_transform<T>(src, dst, mix_duration, tween);\r
                });\r
        }\r
-       \r
-       void apply_image_transform(const std::function<core::image_transform(const core::image_transform&)>& transform, int mix_duration, const std::wstring& tween)\r
+               \r
+       template<typename T>\r
+       void apply_transform(const std::function<T(const T&)>& transform, int mix_duration, const std::wstring& tween)\r
        {\r
                return executor_.invoke([&]\r
                {\r
-                       auto src = root_image_transform_.fetch();\r
-                       auto dst = transform(src);\r
-                       root_image_transform_ = tweened_transform<core::image_transform>(src, dst, mix_duration, tween);\r
-               });\r
-       }\r
+                       auto& root = boost::fusion::at_key<T>(root_transforms_);\r
 \r
-       void apply_audio_transform(const std::function<core::audio_transform(core::audio_transform)>& transform, int mix_duration, const std::wstring& tween)\r
-       {\r
-               return executor_.invoke([&]\r
-               {\r
-                       auto src = root_audio_transform_.fetch();\r
+                       auto src = root.fetch();\r
                        auto dst = transform(src);\r
-                       root_audio_transform_ = tweened_transform<core::audio_transform>(src, dst, mix_duration, tween);\r
+                       root = tweened_transform<T>(src, dst, mix_duration, tween);\r
                });\r
        }\r
-\r
-       void apply_image_transform(int index, const std::function<core::image_transform(core::image_transform)>& transform, int mix_duration, const std::wstring& tween)\r
+               \r
+       template<typename T>\r
+       void apply_transform(int index, const std::function<T(T)>& transform, int mix_duration, const std::wstring& tween)\r
        {\r
                executor_.invoke([&]\r
                {\r
-                       auto src = image_transforms_[index].fetch();\r
-                       auto dst = transform(src);\r
-                       image_transforms_[index] = tweened_transform<core::image_transform>(src, dst, mix_duration, tween);\r
-               });\r
-       }\r
+                       auto& transforms = boost::fusion::at_key<T>(transforms_);\r
 \r
-       void apply_audio_transform(int index, const std::function<core::audio_transform(core::audio_transform)>& transform, int mix_duration, const std::wstring& tween)\r
-       {\r
-               executor_.invoke([&]\r
-               {\r
-                       auto src = audio_transforms_[index].fetch();\r
+                       auto src = transforms[index].fetch();\r
                        auto dst = transform(src);\r
-                       audio_transforms_[index] = tweened_transform<core::audio_transform>(src, dst, mix_duration, tween);\r
+                       transforms[index] = tweened_transform<T>(src, dst, mix_duration, tween);\r
                });\r
        }\r
 \r
-       void reset_image_transform(int mix_duration, const std::wstring& tween)\r
+       template<typename T>\r
+       void reset_transform(int mix_duration, const std::wstring& tween)\r
        {\r
                executor_.invoke([&]\r
                {\r
-                       BOOST_FOREACH(auto& t, image_transforms_)                       \r
-                                t.second = tweened_transform<core::image_transform>(t.second.fetch(), core::image_transform(), mix_duration, tween);                   \r
-                       root_image_transform_ = tweened_transform<core::image_transform>(root_image_transform_.fetch(), core::image_transform(), mix_duration, tween);\r
+                       auto& transforms = boost::fusion::at_key<T>(transforms_);\r
+\r
+                       BOOST_FOREACH(auto& t, transforms)                      \r
+                                t.second = tweened_transform<T>(t.second.fetch(), T(), mix_duration, tween);                   \r
+                       set_transform(T(), mix_duration, tween);\r
                });\r
        }\r
 \r
-       void reset_audio_transform(int mix_duration, const std::wstring& tween)\r
+       template<typename T>\r
+       void reset_transform(int index, int mix_duration, const std::wstring& tween)\r
        {\r
                executor_.invoke([&]\r
-               {\r
-                       BOOST_FOREACH(auto& t, audio_transforms_)\r
-                               t.second = tweened_transform<core::audio_transform>(t.second.fetch(), core::audio_transform(), mix_duration, tween);\r
-                       root_audio_transform_ = tweened_transform<core::audio_transform>(root_audio_transform_.fetch(), core::audio_transform(), mix_duration, tween);\r
+               {               \r
+                       set_transform(T(), mix_duration, tween);\r
                });\r
        }\r
-\r
+       \r
        std::wstring print() const\r
        {\r
                return L"frame_mixer_device";\r
@@ -299,15 +286,17 @@ safe_ptr<core::write_frame> frame_mixer_device::create_frame(void* tag, core::pi
        desc.planes.push_back( core::pixel_format_desc::plane(get_video_format_desc().width, get_video_format_desc().height, 4));\r
        return create_frame(tag, desc);\r
 }\r
-void frame_mixer_device::set_image_transform(const core::image_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_image_transform(transform, mix_duration, tween);}\r
-void frame_mixer_device::set_image_transform(int index, const core::image_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_image_transform(index, transform, mix_duration, tween);}\r
-void frame_mixer_device::set_audio_transform(const core::audio_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_audio_transform(transform, mix_duration, tween);}\r
-void frame_mixer_device::set_audio_transform(int index, const core::audio_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_audio_transform(index, transform, mix_duration, tween);}\r
-void frame_mixer_device::apply_image_transform(const std::function<core::image_transform(core::image_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_image_transform(transform, mix_duration, tween);}\r
-void frame_mixer_device::apply_image_transform(int index, const std::function<core::image_transform(core::image_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_image_transform(index, transform, mix_duration, tween);}\r
-void frame_mixer_device::apply_audio_transform(const std::function<core::audio_transform(core::audio_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_audio_transform(transform, mix_duration, tween);}\r
-void frame_mixer_device::apply_audio_transform(int index, const std::function<core::audio_transform(core::audio_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_audio_transform(index, transform, mix_duration, tween);}\r
-void frame_mixer_device::reset_image_transform(int mix_duration, const std::wstring& tween){impl_->reset_image_transform(mix_duration, tween);}\r
-void frame_mixer_device::reset_audio_transform(int mix_duration, const std::wstring& tween){impl_->reset_audio_transform(mix_duration, tween);}\r
+void frame_mixer_device::set_image_transform(const core::image_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform<core::image_transform>(transform, mix_duration, tween);}\r
+void frame_mixer_device::set_image_transform(int index, const core::image_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform<core::image_transform>(index, transform, mix_duration, tween);}\r
+void frame_mixer_device::set_audio_transform(const core::audio_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform<core::audio_transform>(transform, mix_duration, tween);}\r
+void frame_mixer_device::set_audio_transform(int index, const core::audio_transform& transform, int mix_duration, const std::wstring& tween){impl_->set_transform<core::audio_transform>(index, transform, mix_duration, tween);}\r
+void frame_mixer_device::apply_image_transform(const std::function<core::image_transform(core::image_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform<core::image_transform>(transform, mix_duration, tween);}\r
+void frame_mixer_device::apply_image_transform(int index, const std::function<core::image_transform(core::image_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform<core::image_transform>(index, transform, mix_duration, tween);}\r
+void frame_mixer_device::apply_audio_transform(const std::function<core::audio_transform(core::audio_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform<core::audio_transform>(transform, mix_duration, tween);}\r
+void frame_mixer_device::apply_audio_transform(int index, const std::function<core::audio_transform(core::audio_transform)>& transform, int mix_duration, const std::wstring& tween){impl_->apply_transform<core::audio_transform>(index, transform, mix_duration, tween);}\r
+void frame_mixer_device::reset_image_transform(int mix_duration, const std::wstring& tween){impl_->reset_transform<core::image_transform>(mix_duration, tween);}\r
+void frame_mixer_device::reset_image_transform(int index, int mix_duration, const std::wstring& tween){impl_->reset_transform<core::image_transform>(index, mix_duration, tween);}\r
+void frame_mixer_device::reset_audio_transform(int mix_duration, const std::wstring& tween){impl_->reset_transform<core::audio_transform>(mix_duration, tween);}\r
+void frame_mixer_device::reset_audio_transform(int index, int mix_duration, const std::wstring& tween){impl_->reset_transform<core::audio_transform>(index, mix_duration, tween);}\r
 \r
 }}
\ No newline at end of file
index 285fe22d1260d3df28f0ccca4a22fde9c303202f..be4de85e5c1eac4ec171f182cc7884608a9ce1cb 100644 (file)
@@ -66,7 +66,9 @@ public:
        void apply_audio_transform(int index, const std::function<core::audio_transform(core::audio_transform)>& transform, int mix_duration = 0, const std::wstring& tween = L"linear");\r
 \r
        void reset_image_transform(int mix_duration = 0, const std::wstring& tween = L"linear");\r
+       void reset_image_transform(int index, int mix_duration = 0, const std::wstring& tween = L"linear");\r
        void reset_audio_transform(int mix_duration = 0, const std::wstring& tween = L"linear");\r
+       void reset_audio_transform(int index, int mix_duration = 0, const std::wstring& tween = L"linear");\r
 \r
 private:\r
        struct implementation;\r
index 498ce907e989e728efe785e01dc33fe670be3fd6..1fd15c53dc16817d1b91c8bb3fb826fa67f76452 100644 (file)
@@ -179,8 +179,8 @@ bool MixerCommand::DoExecute()
                {\r
                        if(_parameters[1] == L"OPACITY")\r
                        {\r
-                               int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
-                               std::wstring tween = _parameters.size() > 4 ? _parameters[4] : L"linear";\r
+                               int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+                               std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
 \r
                                double value = boost::lexical_cast<double>(_parameters.at(2));\r
                        \r
@@ -198,8 +198,8 @@ bool MixerCommand::DoExecute()
                        }\r
                        else if(_parameters[1] == L"GAIN")\r
                        {\r
-                               int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
-                               std::wstring tween = _parameters.size() > 4 ? _parameters[4] : L"linear";\r
+                               int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+                               std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
                                double value = boost::lexical_cast<double>(_parameters.at(2));\r
                                \r
                                auto transform = [=](image_transform transform) -> image_transform\r
@@ -216,8 +216,8 @@ bool MixerCommand::DoExecute()
                        }\r
                        else if(_parameters[1] == L"FILL_RECT")\r
                        {\r
-                               int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
-                               std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear";\r
+                               int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
+                               std::wstring tween = _parameters.size() > 6 ? _parameters[7] : L"linear";\r
                                double x        = boost::lexical_cast<double>(_parameters.at(2));\r
                                double y        = boost::lexical_cast<double>(_parameters.at(3));\r
                                double x_s      = boost::lexical_cast<double>(_parameters.at(4));\r
@@ -240,8 +240,8 @@ bool MixerCommand::DoExecute()
                        }\r
                        else if(_parameters[1] == L"KEY_RECT")\r
                        {\r
-                               int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
-                               std::wstring tween = _parameters.size() > 7 ? _parameters[7] : L"linear";\r
+                               int duration = _parameters.size() > 5 ? lexical_cast_or_default(_parameters[6], 0) : 0;\r
+                               std::wstring tween = _parameters.size() > 6 ? _parameters[7] : L"linear";\r
                                double x        = boost::lexical_cast<double>(_parameters.at(2));\r
                                double y        = boost::lexical_cast<double>(_parameters.at(3));\r
                                double x_s      = boost::lexical_cast<double>(_parameters.at(4));\r
@@ -262,15 +262,15 @@ bool MixerCommand::DoExecute()
                        }\r
                        else if(_parameters[1] == L"GRID")\r
                        {\r
-                               int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
-                               std::wstring tween = _parameters.size() > 4 ? _parameters[4] : L"linear";\r
+                               int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+                               std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
                                int n = boost::lexical_cast<int>(_parameters.at(2));\r
                                double delta = 1.0/static_cast<double>(n);\r
                                for(int x = 0; x < n; ++x)\r
                                {\r
                                        for(int y = 0; y < n; ++y)\r
                                        {\r
-                                               int index = x+y*n;\r
+                                               int index = x+y*n+1;\r
                                                auto transform = [=](image_transform transform) -> image_transform\r
                                                {                               \r
                                                        transform.set_fill_translation(x*delta, y*delta);\r
@@ -286,14 +286,21 @@ bool MixerCommand::DoExecute()
                        else if(_parameters[1] == L"RESET")\r
                        {\r
                                int duration = _parameters.size() > 1 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                               GetChannel()->mixer()->reset_image_transform(duration);\r
+                               std::wstring tween = _parameters.size() > 2 ? _parameters[3] : L"linear";\r
+\r
+                               int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
+                               if(layer != std::numeric_limits<int>::min())\r
+                                       GetChannel()->mixer()->reset_image_transform(GetLayerIndex(), duration, tween);\r
+                               else\r
+                                       GetChannel()->mixer()->reset_image_transform(duration, tween);\r
                        }\r
                }\r
                else if(_parameters[0] == L"AUDIO")\r
                {\r
                        if(_parameters[1] == L"GAIN")\r
                        {\r
-                               int duration = _parameters.size() > 3 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+                               int duration = _parameters.size() > 2 ? lexical_cast_or_default(_parameters[3], 0) : 0;\r
+                               std::wstring tween = _parameters.size() > 3 ? _parameters[4] : L"linear";\r
                                double value = boost::lexical_cast<double>(_parameters[2]);\r
 \r
                                auto transform = [=](audio_transform transform) -> audio_transform\r
@@ -304,21 +311,23 @@ bool MixerCommand::DoExecute()
                                \r
                                int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
                                if(layer != std::numeric_limits<int>::min())\r
-                                       GetChannel()->mixer()->apply_audio_transform(GetLayerIndex(), transform, duration);\r
+                                       GetChannel()->mixer()->apply_audio_transform(GetLayerIndex(), transform, duration, tween);\r
                                else\r
-                                       GetChannel()->mixer()->apply_audio_transform(transform, duration);\r
+                                       GetChannel()->mixer()->apply_audio_transform(transform, duration, tween);\r
                        }\r
                        else if(_parameters[1] == L"RESET")\r
                        {\r
                                int duration = _parameters.size() > 1 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                               GetChannel()->mixer()->reset_audio_transform(duration);\r
+                               std::wstring tween = _parameters.size() > 2 ? _parameters[3] : L"linear";\r
+                               GetChannel()->mixer()->reset_audio_transform(duration, tween);\r
                        }\r
                }\r
                else if(_parameters[0] == L"RESET")\r
                {\r
                        int duration = _parameters.size() > 1 ? lexical_cast_or_default(_parameters[2], 0) : 0;\r
-                       GetChannel()->mixer()->reset_image_transform(duration);\r
-                       GetChannel()->mixer()->reset_audio_transform(duration);\r
+                       std::wstring tween = _parameters.size() > 2 ? _parameters[3] : L"linear";\r
+                       GetChannel()->mixer()->reset_image_transform(duration, tween);\r
+                       GetChannel()->mixer()->reset_audio_transform(duration, tween);\r
                }\r
        \r
                SetReplyString(TEXT("202 MIXER OK\r\n"));\r