]> git.sesse.net Git - casparcg/commitdiff
Added support for mixer commands to return current values
authorHelge Norberg <helge.norberg@gmail.com>
Wed, 5 Jun 2013 15:00:16 +0000 (17:00 +0200)
committerHelge Norberg <helge.norberg@gmail.com>
Wed, 5 Jun 2013 15:00:16 +0000 (17:00 +0200)
#141

core/mixer/audio/audio_mixer.cpp
core/mixer/audio/audio_mixer.h
core/mixer/image/blend_modes.cpp
core/mixer/image/blend_modes.h
core/mixer/mixer.cpp
core/mixer/mixer.h
core/producer/stage.cpp
core/producer/stage.h
protocol/amcp/AMCPCommandsImpl.cpp
protocol/amcp/AMCPCommandsImpl.h

index 8959bf4f9d28833511d50b6fcced6c5ba8e7a58b..803e218b8d8d0c07292c033ed1d146905bd24902 100644 (file)
@@ -144,6 +144,11 @@ public:
                transform_stack_.pop();\r
        }\r
 \r
+       float get_master_volume() const\r
+       {\r
+               return master_volume_;\r
+       }\r
+\r
        void set_master_volume(float volume)\r
        {\r
                master_volume_ = volume;\r
@@ -264,6 +269,7 @@ audio_mixer::audio_mixer(const safe_ptr<diagnostics::graph>& graph) : impl_(new
 void audio_mixer::begin(core::basic_frame& frame){impl_->begin(frame);}\r
 void audio_mixer::visit(core::write_frame& frame){impl_->visit(frame);}\r
 void audio_mixer::end(){impl_->end();}\r
+float audio_mixer::get_master_volume() const { return impl_->get_master_volume(); }\r
 void audio_mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); }\r
 audio_buffer audio_mixer::operator()(const video_format_desc& format_desc, const channel_layout& layout){return impl_->mix(format_desc, layout);}\r
 \r
index 68df6aa674ff2cfdbab8f546338935f9b07f84a2..90cdafcb9405e1b6144f610845aa282c8a5534da 100644 (file)
@@ -55,6 +55,7 @@ public:
        virtual void visit(core::write_frame& frame);\r
        virtual void end();\r
 \r
+       float get_master_volume() const;\r
        void set_master_volume(float volume);\r
 \r
        audio_buffer operator()(const video_format_desc& format_desc, const channel_layout& layout);\r
index 3ed386fd2b8cca7ef876b2a5fed8a89bd86ecbc1..73424b8ae12ac7749aaf0b183d887eb9034f518c 100644 (file)
@@ -91,9 +91,76 @@ blend_mode::type get_blend_mode(const std::wstring& str)
        return blend_mode::normal;\r
 }\r
 \r
+std::wstring get_blend_mode(blend_mode::type mode)\r
+{\r
+       switch (mode)\r
+       {\r
+       case blend_mode::normal:\r
+               return L"normal";\r
+       case blend_mode::lighten:\r
+               return L"lighten";\r
+       case blend_mode::darken:\r
+               return L"darken";\r
+       case blend_mode::multiply:\r
+               return L"multiply";\r
+       case blend_mode::average:\r
+               return L"average";\r
+       case blend_mode::add:\r
+               return L"add";\r
+       case blend_mode::subtract:\r
+               return L"subtract";\r
+       case blend_mode::difference:\r
+               return L"difference";\r
+       case blend_mode::negation:\r
+               return L"negation";\r
+       case blend_mode::exclusion:\r
+               return L"exclusion";\r
+       case blend_mode::screen:\r
+               return L"screen";\r
+       case blend_mode::overlay:\r
+               return L"overlay";\r
+       case blend_mode::soft_light:\r
+               return L"soft_light";\r
+       case blend_mode::hard_light:\r
+               return L"hard_light";\r
+       case blend_mode::color_dodge:\r
+               return L"color_dodge";\r
+       case blend_mode::color_burn:\r
+               return L"color_burn";\r
+       case blend_mode::linear_dodge:\r
+               return L"linear_dodge";\r
+       case blend_mode::linear_burn:\r
+               return L"linear_burn";\r
+       case blend_mode::linear_light:\r
+               return L"linear_light";\r
+       case blend_mode::vivid_light:\r
+               return L"vivid_ligh";\r
+       case blend_mode::pin_light:\r
+               return L"pin_light";\r
+       case blend_mode::hard_mix:\r
+               return L"hard_mix";\r
+       case blend_mode::reflect:\r
+               return L"reflect";\r
+       case blend_mode::glow:\r
+               return L"glow";\r
+       case blend_mode::phoenix:\r
+               return L"phoenix";\r
+       case blend_mode::contrast:\r
+               return L"contrast";\r
+       case blend_mode::saturation:\r
+               return L"saturation";\r
+       case blend_mode::color:\r
+               return L"color";\r
+       case blend_mode::luminosity:\r
+               return L"luminosity";\r
+       default:\r
+               return L"normal";\r
+       }\r
+}\r
+\r
 chroma::type get_chroma_mode(const std::wstring& str)\r
 {\r
-    if     (boost::iequals(str, L"none"))\r
+    if (boost::iequals(str, L"none"))\r
         return chroma::none;\r
     else if(boost::iequals(str, L"red"))\r
         return chroma::red;\r
@@ -111,4 +178,27 @@ chroma::type get_chroma_mode(const std::wstring& str)
     return chroma::none;\r
 }\r
 \r
+std::wstring get_chroma_mode(chroma::type type)\r
+{\r
+       switch (type)\r
+       {\r
+       case chroma::none:\r
+               return L"none";\r
+       case chroma::red:\r
+               return L"red";\r
+       case chroma::yellow:\r
+               return L"yellow";\r
+       case chroma::green:\r
+               return L"green";\r
+       case chroma::torquise:\r
+               return L"torquise";\r
+       case chroma::blue:\r
+               return L"blue";\r
+       case chroma::magenta:\r
+               return L"magenta";\r
+       default:\r
+               return L"none";\r
+       }\r
+}\r
+\r
 }}\r
index a0cbb8caf2083a593096d1601d82d88c4a02be41..55a07a5745881500e56685a43ff0c9a6f21511f4 100644 (file)
@@ -36,16 +36,22 @@ struct chroma
         magenta  = 0xff00ff\r
     };\r
 \r
-    unsigned int    key;\r
-    float           threshold,\r
-                    softness,\r
-                    spill,\r
-                    blur;\r
-    bool            show_mask;\r
+    type       key;\r
+    float      threshold;\r
+    float      softness;\r
+       float   spill;\r
+    float      blur;\r
+    bool       show_mask;\r
 \r
-    chroma(type m=none)\r
-    : key(m), threshold(0.0), softness(0.0), spill(0.0),\r
-      blur(0.0), show_mask(false) {}\r
+    chroma(type m = none)\r
+               : key(m)\r
+               , threshold(0.0)\r
+               , softness(0.0)\r
+               , spill(0.0)\r
+               , blur(0.0)\r
+               , show_mask(false)\r
+       {\r
+       }\r
 };\r
 \r
 struct blend_mode\r
@@ -92,7 +98,9 @@ struct blend_mode
 };\r
 \r
 blend_mode::type get_blend_mode(const std::wstring& str);\r
+std::wstring get_blend_mode(blend_mode::type mode);\r
 \r
 chroma::type get_chroma_mode(const std::wstring& str);\r
+std::wstring get_chroma_mode(chroma::type type);\r
 \r
 }}\r
index c5f2e7c74035eed477acad732c90bf5d08782238..4b286418f02fd5360fd2aa18acc77cd44a107018 100644 (file)
@@ -131,6 +131,14 @@ public:
        {               \r
                return make_safe<write_frame>(ogl_, tag, desc, audio_channel_layout);\r
        }\r
+\r
+       blend_mode::type get_blend_mode(int index)\r
+       {\r
+               return executor_.invoke([=]\r
+               {\r
+                       return blend_modes_[index].mode;\r
+               });\r
+       }\r
                                \r
        void set_blend_mode(int index, blend_mode::type value)\r
        {\r
@@ -156,6 +164,14 @@ public:
                }, high_priority);\r
        }\r
 \r
+       chroma get_chroma(int index)\r
+       {\r
+               return executor_.invoke([=]\r
+               {\r
+                       return blend_modes_[index].chroma;\r
+               });\r
+       }\r
+\r
     void set_chroma(int index, const chroma & value)\r
     {\r
         executor_.begin_invoke([=]\r
@@ -164,6 +180,14 @@ public:
         }, high_priority);\r
     }\r
 \r
+       float get_master_volume()\r
+       {\r
+               return executor_.invoke([=]\r
+               {\r
+                       return audio_mixer_.get_master_volume();\r
+               });\r
+       }\r
+\r
        void set_master_volume(float volume)\r
        {\r
                executor_.begin_invoke([=]\r
@@ -200,10 +224,13 @@ mixer::mixer(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<target_t>
 void mixer::send(const std::pair<std::map<int, safe_ptr<core::basic_frame>>, std::shared_ptr<void>>& frames){ impl_->send(frames);}\r
 core::video_format_desc mixer::get_video_format_desc() const { return impl_->get_video_format_desc(); }\r
 safe_ptr<core::write_frame> mixer::create_frame(const void* tag, const core::pixel_format_desc& desc, const channel_layout& audio_channel_layout){ return impl_->create_frame(tag, desc, audio_channel_layout); }              \r
+blend_mode::type mixer::get_blend_mode(int index) { return impl_->get_blend_mode(index); }\r
 void mixer::set_blend_mode(int index, blend_mode::type value){impl_->set_blend_mode(index, value);}\r
+chroma mixer::get_chroma(int index) { return impl_->get_chroma(index); }\r
 void mixer::set_chroma(int index, const chroma & value){impl_->set_chroma(index, value);}\r
 void mixer::clear_blend_mode(int index) { impl_->clear_blend_mode(index); }\r
 void mixer::clear_blend_modes() { impl_->clear_blend_modes(); }\r
+float mixer::get_master_volume() { return impl_->get_master_volume(); }\r
 void mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); }\r
 void mixer::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
 boost::unique_future<boost::property_tree::wptree> mixer::info() const{return impl_->info();}\r
index 185ab38e443af3dbc3febcdb3b531eb3763f3258..d854f7b1ded5023feb59c333e1a96013edb84f2b 100644 (file)
@@ -67,11 +67,14 @@ public:
        core::video_format_desc get_video_format_desc() const; // nothrow\r
        void set_video_format_desc(const video_format_desc& format_desc);\r
        \r
+       blend_mode::type get_blend_mode(int index);\r
        void set_blend_mode(int index, blend_mode::type value);\r
-    void set_chroma(int index, const chroma & value);\r
+       chroma get_chroma(int index);\r
+    void set_chroma(int index, const chroma& value);\r
        void clear_blend_mode(int index);\r
        void clear_blend_modes();\r
 \r
+       float get_master_volume();\r
        void set_master_volume(float volume);\r
 \r
        boost::unique_future<boost::property_tree::wptree> info() const;\r
index 7cb9ef9b2d8292c2453067e01eaca8f7b8a99939..57f7b5834e000547c9dbea728747b5fdf3abfda2 100644 (file)
@@ -230,6 +230,14 @@ public:
                        transforms_.clear();\r
                }, high_priority);\r
        }\r
+\r
+       frame_transform get_current_transform(int index)\r
+       {\r
+               return executor_.invoke([=]\r
+               {\r
+                       return transforms_[index].fetch();\r
+               });\r
+       }\r
                \r
        layer& get_layer(int index)\r
        {\r
@@ -419,6 +427,7 @@ void stage::apply_transforms(const std::vector<stage::transform_tuple_t>& transf
 void stage::apply_transform(int index, const std::function<core::frame_transform(core::frame_transform)>& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform(index, transform, mix_duration, tween);}\r
 void stage::clear_transforms(int index){impl_->clear_transforms(index);}\r
 void stage::clear_transforms(){impl_->clear_transforms();}\r
+frame_transform stage::get_current_transform(int index) { return impl_->get_current_transform(index); }\r
 void stage::spawn_token(){impl_->spawn_token();}\r
 void stage::load(int index, const safe_ptr<frame_producer>& producer, bool preview, int auto_play_delta){impl_->load(index, producer, preview, auto_play_delta);}\r
 void stage::pause(int index){impl_->pause(index);}\r
index eb55da3acf2c17c87a100e5e3edffecc9dcb0a19..f8b2505ac4bc6879bbc27306bf0b9a45226fb2f7 100644 (file)
@@ -60,6 +60,7 @@ public:
        void apply_transform(int index, const transform_func_t& transform, unsigned int mix_duration = 0, const std::wstring& tween = L"linear");\r
        void clear_transforms(int index);\r
        void clear_transforms();\r
+       frame_transform get_current_transform(int index);\r
 \r
        void spawn_token();\r
                        \r
index 0f4bc0a71de14b0a3afe52efbd957f2626d1efcb..09562ea07a968063a15d2fa8419dd6d2c1f06591 100644 (file)
@@ -426,8 +426,14 @@ bool CallCommand::DoExecute()
 // UGLY HACK\r
 tbb::concurrent_unordered_map<int, std::vector<stage::transform_tuple_t>> deferred_transforms;\r
 \r
+core::frame_transform MixerCommand::get_current_transform()\r
+{\r
+       return GetChannel()->stage()->get_current_transform(GetLayerIndex());\r
+}\r
+\r
 bool MixerCommand::DoExecute()\r
-{      \r
+{\r
+       using boost::lexical_cast;\r
        //Perform loading of the clip\r
        try\r
        {       \r
@@ -439,6 +445,9 @@ bool MixerCommand::DoExecute()
 \r
                if(_parameters[0] == L"KEYER" || _parameters[0] == L"IS_KEY")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                               return reply_value([](const frame_transform& t) { return t.is_key ? 1 : 0; });\r
+\r
                        bool value = boost::lexical_cast<int>(_parameters.at(1));\r
                        transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform\r
                        {\r
@@ -448,6 +457,9 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"OPACITY")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                               return reply_value([](const frame_transform& t) { return t.opacity; });\r
+\r
                        int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
                        std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
 \r
@@ -461,6 +473,20 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"FILL" || _parameters[0] == L"FILL_RECT")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                       {\r
+                               auto transform = get_current_transform();\r
+                               auto translation = transform.fill_translation;\r
+                               auto scale = transform.fill_scale;\r
+                               SetReplyString(\r
+                                               L"201 MIXER OK\r\n" \r
+                                               + lexical_cast<std::wstring>(translation[0]) + L" "\r
+                                               + lexical_cast<std::wstring>(translation[1]) + L" "\r
+                                               + lexical_cast<std::wstring>(scale[0]) + L" "\r
+                                               + lexical_cast<std::wstring>(scale[1]) + L"\r\n");\r
+                               return true;\r
+                       }\r
+\r
                        int duration = _parameters.size() > 5 ? boost::lexical_cast<int>(_parameters[5]) : 0;\r
                        std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
                        double x        = boost::lexical_cast<double>(_parameters.at(1));\r
@@ -479,6 +505,20 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"CLIP" || _parameters[0] == L"CLIP_RECT")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                       {\r
+                               auto transform = get_current_transform();\r
+                               auto translation = transform.clip_translation;\r
+                               auto scale = transform.clip_scale;\r
+                               SetReplyString(\r
+                                               L"201 MIXER OK\r\n" \r
+                                               + lexical_cast<std::wstring>(translation[0]) + L" "\r
+                                               + lexical_cast<std::wstring>(translation[1]) + L" "\r
+                                               + lexical_cast<std::wstring>(scale[0]) + L" "\r
+                                               + lexical_cast<std::wstring>(scale[1]) + L"\r\n");\r
+                               return true;\r
+                       }\r
+\r
                        int duration = _parameters.size() > 5 ? boost::lexical_cast<int>(_parameters[5]) : 0;\r
                        std::wstring tween = _parameters.size() > 6 ? _parameters[6] : L"linear";\r
                        double x        = boost::lexical_cast<double>(_parameters.at(1));\r
@@ -528,6 +568,15 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"BLEND")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                       {\r
+                               auto blend_mode = GetChannel()->mixer()->get_blend_mode(GetLayerIndex());\r
+                               SetReplyString(L"201 MIXER OK\r\n" \r
+                                       + lexical_cast<std::wstring>(get_blend_mode(blend_mode)) \r
+                                       + L"\r\n");\r
+                               return true;\r
+                       }\r
+\r
                        auto blend_str = _parameters.at(1);                                                             \r
                        int layer = GetLayerIndex();\r
                        blend_mode::type && blend = get_blend_mode(blend_str);\r
@@ -535,23 +584,54 @@ bool MixerCommand::DoExecute()
                }\r
         else if(_parameters[0] == L"CHROMA")\r
         {\r
-            int layer = GetLayerIndex();\r
+                       if (_parameters.size() == 1)\r
+                       {\r
+                               auto chroma = GetChannel()->mixer()->get_chroma(GetLayerIndex());\r
+                               SetReplyString(L"201 MIXER OK\r\n" \r
+                                       + get_chroma_mode(chroma.key)\r
+                                       + (chroma.key == chroma::none\r
+                                               ? L""\r
+                                               : L" "\r
+                                               + lexical_cast<std::wstring>(chroma.threshold) + L" "\r
+                                               + lexical_cast<std::wstring>(chroma.softness))\r
+                                       + L"\r\n");\r
+                                       // Add the rest when they are actually used and documented\r
+                               return true;\r
+                       }\r
+\r
+                       int layer = GetLayerIndex();\r
             chroma  chroma;\r
-            chroma.key          = get_chroma_mode(_parameters[1]);\r
-            chroma.threshold    = boost::lexical_cast<double>(_parameters[2]);\r
-            chroma.softness     = boost::lexical_cast<double>(_parameters[3]);\r
-            chroma.spill        = _parameters.size() > 4 ? boost::lexical_cast<double>(_parameters[4]) : 0.0f;\r
-            chroma.blur         = _parameters.size() > 5 ? boost::lexical_cast<double>(_parameters[5]) : 0.0f;\r
-            chroma.show_mask    = _parameters.size() > 6 ? bool(boost::lexical_cast<int>(_parameters[6])) : false;\r
+            chroma.key = get_chroma_mode(_parameters[1]);\r
+\r
+                       if (chroma.key != chroma::none)\r
+                       {\r
+                               chroma.threshold    = boost::lexical_cast<double>(_parameters[2]);\r
+                               chroma.softness     = boost::lexical_cast<double>(_parameters[3]);\r
+                               chroma.spill        = _parameters.size() > 4 ? boost::lexical_cast<double>(_parameters[4]) : 0.0f;\r
+                               chroma.blur         = _parameters.size() > 5 ? boost::lexical_cast<double>(_parameters[5]) : 0.0f;\r
+                               chroma.show_mask    = _parameters.size() > 6 ? bool(boost::lexical_cast<int>(_parameters[6])) : false;\r
+                       }\r
+\r
             GetChannel()->mixer()->set_chroma(GetLayerIndex(), chroma);\r
         }\r
                else if(_parameters[0] == L"MASTERVOLUME")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                       {\r
+                               auto volume = GetChannel()->mixer()->get_master_volume();\r
+                               SetReplyString(L"201 MIXER OK\r\n" \r
+                                       + lexical_cast<std::wstring>(volume) + L"\r\n");\r
+                               return true;\r
+                       }\r
+\r
                        float master_volume = boost::lexical_cast<float>(_parameters.at(1));\r
                        GetChannel()->mixer()->set_master_volume(master_volume);\r
                }\r
                else if(_parameters[0] == L"BRIGHTNESS")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                               return reply_value([](const frame_transform& t) { return t.brightness; });\r
+\r
                        auto value = boost::lexical_cast<double>(_parameters.at(1));\r
                        int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
                        std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
@@ -563,6 +643,9 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"SATURATION")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                               return reply_value([](const frame_transform& t) { return t.saturation; });\r
+\r
                        auto value = boost::lexical_cast<double>(_parameters.at(1));\r
                        int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
                        std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
@@ -574,6 +657,9 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"CONTRAST")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                               return reply_value([](const frame_transform& t) { return t.contrast; });\r
+\r
                        auto value = boost::lexical_cast<double>(_parameters.at(1));\r
                        int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
                        std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
@@ -585,6 +671,18 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"LEVELS")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                       {\r
+                               auto levels = get_current_transform().levels;\r
+                               SetReplyString(L"201 MIXER OK\r\n"\r
+                                       + lexical_cast<std::wstring>(levels.min_input) + L" "\r
+                                       + lexical_cast<std::wstring>(levels.max_input) + L" "\r
+                                       + lexical_cast<std::wstring>(levels.gamma) + L" "\r
+                                       + lexical_cast<std::wstring>(levels.min_output) + L" "\r
+                                       + lexical_cast<std::wstring>(levels.max_output) + L"\r\n");\r
+                               return true;\r
+                       }\r
+\r
                        levels value;\r
                        value.min_input  = boost::lexical_cast<double>(_parameters.at(1));\r
                        value.max_input  = boost::lexical_cast<double>(_parameters.at(2));\r
@@ -602,6 +700,9 @@ bool MixerCommand::DoExecute()
                }\r
                else if(_parameters[0] == L"VOLUME")\r
                {\r
+                       if (_parameters.size() == 1)\r
+                               return reply_value([](const frame_transform& t) { return t.volume; });\r
+\r
                        int duration = _parameters.size() > 2 ? boost::lexical_cast<int>(_parameters[2]) : 0;\r
                        std::wstring tween = _parameters.size() > 3 ? _parameters[3] : L"linear";\r
                        double value = boost::lexical_cast<double>(_parameters[1]);\r
index 788887a337ee569e3eb588492fecc20407cc1e07..2e7f0d4718eed43b3f49834af373c8ed77ad8a3a 100644 (file)
 \r
 #include "AMCPCommand.h"\r
 \r
-namespace caspar { namespace protocol {\r
+namespace caspar {\r
+namespace core {\r
+       struct frame_transform;\r
+}\r
+\r
+namespace protocol {\r
        \r
 std::wstring ListMedia();\r
 std::wstring ListTemplates();\r
@@ -53,6 +58,17 @@ class CallCommand : public AMCPCommandBase<true, AddToQueue, 1>
 class MixerCommand : public AMCPCommandBase<true, AddToQueue, 1>\r
 {\r
        std::wstring print() const { return L"MixerCommand";}\r
+       core::frame_transform get_current_transform();\r
+       template<typename Func>\r
+       bool reply_value(const Func& extractor)\r
+       {\r
+               auto value = extractor(get_current_transform());\r
+\r
+               SetReplyString(L"201 MIXER OK\r\n"\r
+                       + boost::lexical_cast<std::wstring>(value) + L"\r\n");\r
+\r
+               return true;\r
+       }\r
        bool DoExecute();\r
 };\r
        \r