1 // madronalib: a C++ framework for DSP applications.
2 // Copyright (c) 2020 Madrona Labs LLC. http://www.madronalabs.com
3 // Distributed under the MIT license: http://madrona-labs.mit-license.org/
4 
5 #pragma once
6 
7 #include <list>
8 #include <map>
9 #include <string>
10 
11 #include "MLMatrix.h"
12 #include "MLPath.h"
13 #include "MLSymbol.h"
14 #include "MLText.h"
15 
16 // Value: a modifiable property. Properties have four types: undefined, float,
17 // text, and matrix.
18 
19 // TODO: instead of using Matrix directly here as a type, make a blob type
20 // and utilities (in Matrix) for conversion. This lets the current "model" code
21 // go into "app" because it doesn't depend on DSP math anymore, and increases
22 // reusability.
23 
24 namespace ml
25 {
26 class Value
27 {
28  public:
29   enum Type
30   {
31     kUndefinedValue = 0,
32     kFloatValue = 1,
33     kTextValue = 2,
34     kMatrixValue = 3
35   };
36 
37   static const Matrix nullMatrix;
38 
39   Value();
40   Value(const Value& other);
41   Value& operator=(const Value& other);
42   Value(float v);
43   Value(int v);
44   Value(bool v);
45   Value(long v);
46   Value(double v);
47   Value(const ml::Text& t);
48   Value(const char* t);
49   Value(const ml::Matrix& s);
50 
51   explicit operator bool() const { return mType != kUndefinedValue; }
52 
53   // matrix type constructor via initializer_list
Value(std::initializer_list<float> values)54   Value(std::initializer_list<float> values)
55   {
56     auto size = values.size();
57     if (size == 0)
58     {
59       *this = Value();
60     }
61     else if (size == 1)
62     {
63       *this = Value(*values.begin());
64     }
65     else
66     {
67       *this = Value(Matrix(values));
68     }
69   }
70 
71   ~Value();
72 
getFloatValue()73   inline const float getFloatValue() const { return mFloatVal; }
74 
getFloatValueWithDefault(float d)75   inline const float getFloatValueWithDefault(float d) const
76   {
77     return (mType == kFloatValue) ? mFloatVal : d;
78   }
79 
getBoolValue()80   inline const float getBoolValue() const { return static_cast<bool>(mFloatVal); }
81 
getBoolValueWithDefault(bool b)82   inline const bool getBoolValueWithDefault(bool b) const
83   {
84     return (mType == kFloatValue) ? static_cast<bool>(mFloatVal) : b;
85   }
86 
getIntValue()87   inline const float getIntValue() const { return static_cast<int>(mFloatVal); }
88 
getIntValueWithDefault(int d)89   inline const int getIntValueWithDefault(int d) const
90   {
91     return (mType == kFloatValue) ? static_cast<int>(mFloatVal) : d;
92   }
93 
getTextValue()94   inline const ml::Text getTextValue() const
95   {
96     return (mType == kTextValue) ? (mTextVal) : ml::Text();
97   }
98 
getTextValueWithDefault(Text d)99   inline const ml::Text getTextValueWithDefault(Text d) const
100   {
101     return (mType == kTextValue) ? (mTextVal) : d;
102   }
103 
getMatrixValue()104   inline const Matrix& getMatrixValue() const
105   {
106     return (mType == kMatrixValue) ? (mMatrixVal) : nullMatrix;
107   }
108 
getMatrixValueWithDefault(Matrix d)109   inline const Matrix getMatrixValueWithDefault(Matrix d) const
110   {
111     return (mType == kMatrixValue) ? (mMatrixVal) : d;
112   }
113 
114   // For each type of property, a setValue method must exist
115   // to set the value of the property to that of the argument.
116   //
117   // For each type of property, if the size of the argument is equal to the
118   // size of the current value, the value must be modified in place.
119   // This guarantee keeps DSP graphs from allocating memory as they run.
120   void setValue(const Value& v);
121   void setValue(const float& v);
122   void setValue(const int& v);
123   void setValue(const bool& v);
124   void setValue(const long& v);
125   void setValue(const double& v);
126   void setValue(const ml::Text& v);
127   void setValue(const char* const v);
128   void setValue(const Matrix& v);
129 
130   bool operator==(const Value& b) const;
131   bool operator!=(const Value& b) const;
132 
getType()133   Type getType() const { return mType; }
getTypeAsSymbol()134   Symbol getTypeAsSymbol() const
135   {
136     static Symbol kTypesAsSymbols[4]{"undefined", "float", "text", "matrix"};
137     return kTypesAsSymbols[mType];
138   }
isUndefinedType()139   bool isUndefinedType() { return mType == kUndefinedValue; }
isFloatType()140   bool isFloatType() { return mType == kFloatValue; }
isTextType()141   bool isTextType() { return mType == kTextValue; }
isMatrixType()142   bool isMatrixType() { return mType == kMatrixValue; }
143 
144   bool operator<<(const Value& b) const;
145 
146  private:
147   // TODO reduce storage requirements and reduce copying!
148   // -- this is a minimal-code start
149   Type mType{kUndefinedValue};
150   float mFloatVal{};
151   ml::Text mTextVal{};
152   Matrix mMatrixVal{};
153 };
154 
155 // NamedValue for initializer lists
156 struct NamedValue
157 {
158   ml::Path name{};
159   Value value{};
160 
161   NamedValue() = default;
NamedValueNamedValue162   NamedValue(ml::Path np, Value nv) : name(np), value(nv) {}
163 };
164 
165 // Define a type for initializing a new object with a list of Values.
166 using WithValues = const std::initializer_list<NamedValue>;
167 
168 
169 // utilities
170 
171 // note: this implementation does not disable this overload for array types
172 template <typename T, typename... Args>
make_unique(Args &&...args)173 std::unique_ptr<T> make_unique(Args&&... args)
174 {
175   return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
176 }
177 
178 std::ostream& operator<<(std::ostream& out, const ml::Value& r);
179 
180 }  // namespace ml
181