Index: NetworkEditor/src/ProcessingTree.cxx =================================================================== --- NetworkEditor/src/ProcessingTree.cxx (revision 10025) +++ NetworkEditor/src/ProcessingTree.cxx (working copy) @@ -70,6 +70,7 @@ "ControlTraceReader", "ControlTraceWriter", "ControlScaler", + "ControlOperator", "AutoPanner", "FlagControl", "Random", Index: NetworkEditor/src/RegisterConfiguratorLaunchers.cxx =================================================================== --- NetworkEditor/src/RegisterConfiguratorLaunchers.cxx (revision 10025) +++ NetworkEditor/src/RegisterConfiguratorLaunchers.cxx (working copy) @@ -62,6 +62,7 @@ // Controls #include #include +#include #include #include #include "ControlSurface.hxx" @@ -129,6 +130,7 @@ // Controls STANDARD_PROCESSING_CONFIG_REGISTER(AutoPannerConfig); +STANDARD_PROCESSING_CONFIG_REGISTER(ControlOperatorConfig); STANDARD_PROCESSING_CONFIG_REGISTER(ControlPrinterConfig); STANDARD_PROCESSING_CONFIG_REGISTER(ControlScalerConfig); STANDARD_PROCESSING_CONFIG_REGISTER(ControlSourceConfig); Index: CLAM/src/Processing/Controls/ControlOperator.cxx =================================================================== --- CLAM/src/Processing/Controls/ControlOperator.cxx (revision 0) +++ CLAM/src/Processing/Controls/ControlOperator.cxx (revision 0) @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * Copyright (c) 2007 Superlucidity Services, LLC and Zachary T Welch + * + * + * 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 of the License, 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "ControlOperator.hxx" +#include "Factory.hxx" +#include + +namespace CLAM +{ + +Enum::tEnumValue EControlOpType::sEnumValues[] = + { + { EControlOpType::eAdd, "Add" }, + { EControlOpType::eSubtract, "Subtract" }, + { EControlOpType::eMultiply, "Multiply" }, + { EControlOpType::eDivide, "Divide" }, + { EControlOpType::eSine, "Sine" }, + { EControlOpType::eCosine, "Cosine" }, + { EControlOpType::eTangent, "Tangent" }, + { EControlOpType::eArcSine, "ArcSine" }, + { EControlOpType::eArcCosine, "ArcCosine" }, + { EControlOpType::eArcTangent, "ArcTangent" }, + { EControlOpType::eNatLogarithm, "Natural Logarithm" }, + { EControlOpType::eNatExponent, "Natural Exponent" }, + { 0, NULL } + }; + +Enum::tValue EControlOpType::sDefault = EControlOpType::eAdd; + +/* ================================================================== */ + +void ControlOperatorConfig::DefaultInit() +{ + AddAll(); + UpdateData(); + SetNumberOfInputs(2); +} + +ControlOperator::ControlOperator() + : mOperator(NULL) +{ + Configure( mConfig ); +} + +ControlOperator::ControlOperator( const ControlOperatorConfig& cfg ) + : mOperator(NULL) +{ + Configure( cfg ); +} + +ControlOperator::~ControlOperator() +{ + RemoveOldControls(); + delete mOperator; +} + +bool ControlOperator::ConcreteConfigure( const ProcessingConfig& cfg ) +{ + RemoveOldControls(); + CopyAsConcreteConfig( mConfig, cfg ); + + delete mOperator; + switch (mConfig.GetSelectedOperator().GetValue()) + { + case EControlOpType::eAdd: + mOperator = new ControlOpAdd(); + break; + case EControlOpType::eSubtract: + mOperator = new ControlOpSubtract(); + break; + case EControlOpType::eMultiply: + mOperator = new ControlOpMultiply(); + break; + case EControlOpType::eDivide: + mOperator = new ControlOpDivide(); + break; + + case EControlOpType::eSine: + mOperator = new ControlOpSine(); + break; + case EControlOpType::eCosine: + mOperator = new ControlOpCosine(); + break; + case EControlOpType::eTangent: + mOperator = new ControlOpTangent(); + break; + + case EControlOpType::eArcSine: + mOperator = new ControlOpArcSine(); + break; + case EControlOpType::eArcCosine: + mOperator = new ControlOpArcCosine(); + break; + case EControlOpType::eArcTangent: + mOperator = new ControlOpArcTangent(); + break; + + case EControlOpType::eNatLogarithm: + mOperator = new ControlOpNatLogarithm(); + break; + case EControlOpType::eNatExponent: + mOperator = new ControlOpNatExponent(); + break; + + default: + AddConfigErrorMessage("Invalid operator selected in configuration"); + return false; + } + + int nInputs = int(mConfig.GetNumberOfInputs()); + int minInputs = mOperator->GetMinimumInputs(); + if (nInputs < minInputs) + { + std::stringstream str; + str << "The '" << mOperator->GetOpType().GetString() + << "' operator requires at least " + << minInputs << " inputs"; + AddConfigErrorMessage(str.str()); + return false; + } + + mInControls.Resize(nInputs, "Input", this); + + int nOutputs = mOperator->GetNumberOfOutputs(nInputs); + mOutControls.Resize(nOutputs, "Output", this); + + return true; +} + +bool ControlOperator::Do() +{ + mOperator->Do(mInControls, mOutControls); + return true; +} + +void ControlOperator::RemoveOldControls() +{ + GetInControls().Clear(); + mInControls.Clear(); + GetOutControls().Clear(); + mOutControls.Clear(); +} + +/* ================================================================== */ + +void ControlBinaryOp::Do(const InControlArray &in, OutControlArray &out) const +{ + TControlData output = in[0].GetLastValue(); + size_t nControls = in.Size(); + for (int i = 1; i < nControls; i++) + output = DoOp(output, in[i].GetLastValue()); + out[0].SendControl(output); +} + +void ControlUnaryOp::Do(const InControlArray &in, OutControlArray &out) const +{ + size_t nControls = in.Size(); + for (size_t i = 0; i < nControls; i++) + out[i].SendControl(DoOp(in[i].GetLastValue())); +} + +/* ================================================================== */ + +TControlData ControlOpAdd::DoOp(TControlData lhs, TControlData rhs) const +{ + return lhs + rhs; +} + +TControlData ControlOpSubtract::DoOp(TControlData lhs, TControlData rhs) const +{ + return lhs - rhs; +} + +TControlData ControlOpMultiply::DoOp(TControlData lhs, TControlData rhs) const +{ + return lhs * rhs; +} + +TControlData ControlOpDivide::DoOp(TControlData lhs, TControlData rhs) const +{ + if (0 == rhs) + return INFINITY; + return lhs / rhs; +} + +/* ================================================================== */ + +TControlData ControlOpSine::DoOp(TControlData input) const +{ + return sin(input); +} +TControlData ControlOpCosine::DoOp(TControlData input) const +{ + return cos(input); +} +TControlData ControlOpTangent::DoOp(TControlData input) const +{ + return tan(input); +} + +TControlData ControlOpArcSine::DoOp(TControlData input) const +{ + return asin(input); +} +TControlData ControlOpArcCosine::DoOp(TControlData input) const +{ + return acos(input); +} +TControlData ControlOpArcTangent::DoOp(TControlData input) const +{ + return atan(input); +} + +TControlData ControlOpNatLogarithm::DoOp(TControlData input) const +{ + return log(input); +} +TControlData ControlOpNatExponent::DoOp(TControlData input) const +{ + return exp(input); +} + +/* ================================================================== */ + +namespace detail +{ +typedef CLAM::Factory ProcessingFactory; +static ProcessingFactory::Registrator + regtControlOperator( "ControlOperator" ); +} + +} // CLAM namespace + Index: CLAM/src/Processing/Controls/ControlOperator.hxx =================================================================== --- CLAM/src/Processing/Controls/ControlOperator.hxx (revision 0) +++ CLAM/src/Processing/Controls/ControlOperator.hxx (revision 0) @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * Copyright (c) 2007 Superlucidity Services, LLC and Zachary T Welch + * + * + * 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 of the License, 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _ControlOperator_ +#define _ControlOperator_ + +#include "DataTypes.hxx" +#include "Processing.hxx" +#include "ProcessingConfig.hxx" +#include "InControlArray.hxx" +#include "OutControlArray.hxx" +#include "Enum.hxx" + +namespace CLAM +{ + +class EControlOpType : public Enum +{ +public: + + static tEnumValue sEnumValues[]; + static tValue sDefault; + EControlOpType() : Enum(sEnumValues, sDefault) {} + EControlOpType(tValue v) : Enum(sEnumValues, v) {}; + EControlOpType(std::string s) : Enum(sEnumValues, s) {}; + + typedef enum { + eAdd, + eSubtract, + eMultiply, + eDivide, + eSine, + eCosine, + eTangent, + eArcSine, + eArcCosine, + eArcTangent, + eNatLogarithm, + eNatExponent, + }; + + Component* Species() const { return new EControlOpType; } +}; + +/* ================================================================== */ + +class ControlOperatorConfig : public ProcessingConfig +{ +public: + DYNAMIC_TYPE_USING_INTERFACE( + ControlOperatorConfig, 2, ProcessingConfig ); + DYN_ATTRIBUTE( 0, public, EControlOpType, SelectedOperator ); + DYN_ATTRIBUTE( 1, public, unsigned int, NumberOfInputs ); + +private: + void DefaultInit(); +}; + +class ControlOp; + +class ControlOperator : public Processing +{ +public: + ControlOperator(); + ControlOperator( const ControlOperatorConfig& cfg ); + ~ControlOperator(); + + const char *GetClassName() const { return "ControlOperator"; } + + bool ConcreteConfigure( const ProcessingConfig& cfg ); + + const ProcessingConfig& GetConfig() const { return mConfig; } + + bool Do(); + +protected: + void RemoveOldControls(); + +private: + ControlOperatorConfig mConfig; + InControlArray mInControls; + OutControlArray mOutControls; + + ControlOp *mOperator; +}; + +/* ================================================================== */ + +class ControlOp { +public: + virtual ~ControlOp() { } + virtual EControlOpType GetOpType() const = 0; + virtual size_t GetMinimumInputs() const = 0; + virtual size_t GetNumberOfOutputs(size_t nInputs) const = 0; + + virtual void Do(const InControlArray &in, OutControlArray &out) const = 0; +}; + +/* ================================================================== */ + +class ControlBinaryOp : public ControlOp +{ +public: + size_t GetMinimumInputs() const { return 2; } + virtual size_t GetNumberOfOutputs(size_t nInputs) const { return 1; } + + void Do(const InControlArray &in, OutControlArray &out) const; + +protected: + virtual TControlData DoOp(TControlData lhs, TControlData rhs) const = 0; +}; + +class ControlOpAdd : public ControlBinaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eAdd; } + TControlData DoOp(TControlData lhs, TControlData rhs) const; +}; + +class ControlOpSubtract : public ControlBinaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eSubtract; } + TControlData DoOp(TControlData lhs, TControlData rhs) const; +}; + +class ControlOpMultiply : public ControlBinaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eMultiply; } + TControlData DoOp(TControlData lhs, TControlData rhs) const; +}; + +class ControlOpDivide : public ControlBinaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eDivide; } + TControlData DoOp(TControlData lhs, TControlData rhs) const; +}; + +/* ================================================================== */ + +class ControlUnaryOp : public ControlOp { +public: + size_t GetMinimumInputs() const { return 1; } + virtual size_t GetNumberOfOutputs(size_t nInputs) const { return nInputs; } + + void Do(const InControlArray &in, OutControlArray &out) const; + +protected: + virtual TControlData DoOp(TControlData input) const = 0; +}; + +class ControlOpSine : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eSine; } + TControlData DoOp(TControlData input) const; +}; +class ControlOpCosine : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eCosine; } + TControlData DoOp(TControlData input) const; +}; +class ControlOpTangent : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eTangent; } + TControlData DoOp(TControlData input) const; +}; + +class ControlOpArcSine : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eArcSine; } + TControlData DoOp(TControlData input) const; +}; +class ControlOpArcCosine : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eArcCosine; } + TControlData DoOp(TControlData input) const; +}; +class ControlOpArcTangent : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eArcTangent; } + TControlData DoOp(TControlData input) const; +}; + +class ControlOpNatLogarithm : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eNatLogarithm; } + TControlData DoOp(TControlData input) const; +}; +class ControlOpNatExponent : public ControlUnaryOp +{ +public: + EControlOpType GetOpType() const { return EControlOpType::eNatExponent; } + TControlData DoOp(TControlData input) const; +}; + + +} // CLAM namespace + +#endif Index: CLAM/test/UnitTests/ProcessingsTests/ControlOperatorTest.cxx =================================================================== --- CLAM/test/UnitTests/ProcessingsTests/ControlOperatorTest.cxx (revision 0) +++ CLAM/test/UnitTests/ProcessingsTests/ControlOperatorTest.cxx (revision 0) @@ -0,0 +1,220 @@ +/* +* Copyright (c) 2001-2002 MUSIC TECHNOLOGY GROUP (MTG) +* UNIVERSITAT POMPEU FABRA +* Copyright (c) 2007 Superlucidity Services, LLC and Zachary T Welch +* +* +* 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 of the License, 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 this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +*/ + +#include +#include "ControlOperator.hxx" +#include "OutControlSender.hxx" +#include "ControlPrinter.hxx" +#include "InControl.hxx" +#include "DataTypes.hxx" +#include + +namespace CLAMTest { + +class ControlOperatorTest; +CPPUNIT_TEST_SUITE_REGISTRATION( ControlOperatorTest ); + +class ControlOperatorTest : public CppUnit::TestFixture +{ +public: + + void setUp(){} + void tearDown(){} + +private: + static const CLAM::TControlData xValue = .3141, yValue = .2718; + static const double mDelta = 0.00001f; + + CPPUNIT_TEST_SUITE( ControlOperatorTest ); + CPPUNIT_TEST( testDo_BinaryAdd ); + CPPUNIT_TEST( testDo_BinarySubtract ); + CPPUNIT_TEST( testDo_BinaryMultiply ); + CPPUNIT_TEST( testDo_BinaryDivide ); + CPPUNIT_TEST( testDo_UnarySine ); + CPPUNIT_TEST( testDo_UnaryCosine ); + CPPUNIT_TEST( testDo_UnaryTangent ); + CPPUNIT_TEST( testDo_UnaryArcSine ); + CPPUNIT_TEST( testDo_UnaryArcCosine ); + CPPUNIT_TEST( testDo_UnaryArcTangent ); + CPPUNIT_TEST( testDo_UnaryNatLogarithm ); + CPPUNIT_TEST( testDo_UnaryNatExponent ); + CPPUNIT_TEST_SUITE_END(); + + void configureSender(CLAM::OutControlSender &sender, CLAM::TControlData value) + { + CLAM::OutControlSenderConfig config; + config.SetMin(value); + config.SetDefault(value); + config.SetMax(value); + sender.Configure(config); + } + + void testBinaryOp(CLAM::EControlOpType optype, + CLAM::TControlData expected) + { + CLAM::OutControlSender xSender, ySender; + configureSender(xSender, xValue); + configureSender(ySender, yValue); + + CLAM::ControlOperator op; + CLAM::ControlOperatorConfig config; + config.SetSelectedOperator(optype); + config.SetNumberOfInputs(2); + op.Configure(config); + + CLAM::ControlPrinter out; + CLAM::ControlPrinterConfig pconfig; + out.Configure(pconfig); + + xSender.GetOutControls().Get("out").AddLink( + op.GetInControls().Get("Input_0")); + ySender.GetOutControls().Get("out").AddLink( + op.GetInControls().Get("Input_1")); + op.GetOutControls().Get("Output_0").AddLink( + out.GetInControls().Get("In Control")); + + xSender.Start(); + ySender.Start(); + op.Start(); + out.Start(); + + xSender.Do(); + ySender.Do(); + op.Do(); + out.Do(); + + CLAM::TControlData received = + out.GetInControls().Get("In Control").GetLastValue(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, received, mDelta); + + xSender.Stop(); + ySender.Stop(); + op.Stop(); + out.Stop(); + } + void testDo_BinaryAdd() + { + testBinaryOp(CLAM::EControlOpType::eAdd, xValue + yValue); + } + void testDo_BinarySubtract() + { + testBinaryOp(CLAM::EControlOpType::eSubtract, xValue - yValue); + } + void testDo_BinaryMultiply() + { + testBinaryOp(CLAM::EControlOpType::eMultiply, xValue * yValue); + } + void testDo_BinaryDivide() + { + testBinaryOp(CLAM::EControlOpType::eDivide, xValue / yValue); + } + + void testUnaryOp(CLAM::EControlOpType optype, + CLAM::TControlData expectedX, + CLAM::TControlData expectedY) + { + CLAM::OutControlSender xSender, ySender; + configureSender(xSender, xValue); + configureSender(ySender, yValue); + + CLAM::ControlOperator op; + CLAM::ControlOperatorConfig config; + config.SetSelectedOperator(optype); + config.SetNumberOfInputs(2); + op.Configure(config); + + CLAM::ControlPrinter out; + CLAM::ControlPrinterConfig pconfig; + pconfig.SetIdentifier("out"); + pconfig.SetNumberOfInputs(2); + out.Configure(pconfig); + + xSender.GetOutControls().Get("out").AddLink( + op.GetInControls().Get("Input_0")); + ySender.GetOutControls().Get("out").AddLink( + op.GetInControls().Get("Input_1")); + op.GetOutControls().Get("Output_0").AddLink( + out.GetInControls().Get("out_0")); + op.GetOutControls().Get("Output_1").AddLink( + out.GetInControls().Get("out_1")); + + xSender.Start(); + ySender.Start(); + op.Start(); + out.Start(); + + xSender.Do(); + ySender.Do(); + op.Do(); + out.Do(); + + CLAM::TControlData receivedX = + out.GetInControls().Get("out_0").GetLastValue(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expectedX, receivedX, mDelta); + + CLAM::TControlData receivedY = + out.GetInControls().Get("out_1").GetLastValue(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(expectedY, receivedY, mDelta); + + xSender.Stop(); + ySender.Stop(); + op.Stop(); + out.Stop(); + } + void testDo_UnarySine() + { + testUnaryOp(CLAM::EControlOpType::eSine, sin(xValue), sin(yValue)); + } + void testDo_UnaryCosine() + { + testUnaryOp(CLAM::EControlOpType::eCosine, cos(xValue), cos(yValue)); + } + void testDo_UnaryTangent() + { + testUnaryOp(CLAM::EControlOpType::eTangent, tan(xValue), tan(yValue)); + } + + void testDo_UnaryArcSine() + { + testUnaryOp(CLAM::EControlOpType::eArcSine, asin(xValue), asin(yValue)); + } + void testDo_UnaryArcCosine() + { + testUnaryOp(CLAM::EControlOpType::eArcCosine, acos(xValue), acos(yValue)); + } + void testDo_UnaryArcTangent() + { + testUnaryOp(CLAM::EControlOpType::eArcTangent, atan(xValue), atan(yValue)); + } + + void testDo_UnaryNatLogarithm() + { + testUnaryOp(CLAM::EControlOpType::eNatLogarithm, log(xValue), log(yValue)); + } + void testDo_UnaryNatExponent() + { + testUnaryOp(CLAM::EControlOpType::eNatExponent, exp(xValue), exp(yValue)); + } +}; + +} // CLAMTest namespace +