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