-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCPG.h
283 lines (189 loc) · 9.34 KB
/
CPG.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#pragma once
/*! \class CPG
* \brief Engine: Matsuoka Oscillator Central Pattern Generator Template Class
*
* Instantiates a central pattern generator network with node type
* depending on the type passed to it.
*
* The type passed to the template must have the same interface as my
* MatsuNode class. And my design intention is that type passed will
* inherit from MatsuNode.
*
* For this reason getter methods are sparse, beyond a const child node
* getter, via which all getter methods of child class can be accessed.
*/
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include "matsuNode.h"
#include "ScalingCurve.h"
class CPG
{
public:
/// Data specifying a single input to a node from another node
struct input
{
unsigned sourceID; /*!< Unique identifier of the node which is the source of the input. */
double weight; /*!< Scaling multiplier for the input signal. Typically 0.0 to c5.0 */
double phase; /*!< Phase offset for the input signal. 0.0 to 1.0 */
bool operator == (const input &rhs)
{
// TODO: Re-think this, it was brainless in order to allow python bindings build
return sourceID == rhs.sourceID;
}
};
CPG(unsigned sampleRate);
CPG(double t1, double t2, double c1, double c2, double b, double g);
CPG(double t1, double t2, double c, double b, double g);
~CPG() { uncreate(); };
// on current implementation CPG not suitable to be copied
// would need to move pointers out of matsunode or develop complex copy constructor
// CPG is naturally a singleton object for most uses so no big issue at present
CPG(const CPG& that) = delete;
/// Returns a complete vector of unique identifiers of the nodes in the CPG, sorted ascending
const std::vector<unsigned>& getNodeList() const;
/// Calculates the next state and output values for the Matsuoka oscillators at each node.
void step();
/// Calculates the next state and output values for the indicated node.
void step(unsigned nodeID);
/// Resets the internal state and output value for all _nodes to (stable) initialisation values
/*! Useful should one or more nodes stop responding.
*/
void reset();
/// Resets the internal state and output value for the node to the provided values
/*! Useful should one or more _nodes stop responding, or to restart the whole network
* from a given point
*/
void reset(double x1, double x2, double v1, double v2);
/// Resets the internal state and output value for single nodes to (stable) initialisation values
/*! Useful should one or more nodes stop responding.
*/
void reset(unsigned nodeID);
/// Resets the internal state and output value for single nodes to (stable) initialisation values
/*! Useful should one or more nodes stop responding.
*/
void reset(unsigned nodeID, double x1, double x2, double v1, double v2);
/// if createExternalSyncResources has been run, this function resets the node to its state just prior to positive zero crossing
/// otherwise behaviour is as reset(nodeID)
void zeroSync(unsigned nodeID);
/// Adds, or changes weight of, a one-way connection between two nodes
/*! If the connection specified exists, then its weight is set to that specified.
* Otherwise a new input is added to the node whose ID is specified by nodeTo,
* from the node whose ID is specified by nodeFrom, with weight specified.
* If either node specified does not exist, an exception is thrown.
*/
void setConnection(unsigned nodeFrom, unsigned nodeTo, double weight);
/// Adds, or changes weight of, a reciprocal connection between two nodes
/*! If a connection specified exists, then its weight is set to that specified.
* Otherwise a new input is added: to the node specified by nodeTo,
* from the node specified by nodeFrom, with weight specified.
* If either node specified does not exist, an exception is thrown.
*/
void setConnection(unsigned nodeA, unsigned nodeB, double weightAtoB,
double weightBtoA);
/// Changes phase offset of signal on input from nodeFrom to nodeTo
/*! Phase between 0 and 1.
*/
void setConnectionPhaseOffset(unsigned nodeFrom, unsigned nodeTo,
double phaseOffset);
void removeConnection(unsigned nodeFrom, unsigned nodeTo);
/// Returns a sorted vector of all [inputs](@ref input) for a given node
std::vector<input> getInputs(unsigned nodeID) const;
/// returns const reference to the node whose nodeID is provided.
/*! all read access to nodes is via const functions on node.
* This allows easy extending of the node class' read methods,
* while CPG class retains ownership of write methods.
*/
const MatsuNode& getNode(unsigned nodeID) const;
double getNodeSelfNoise(unsigned nodeID);
bool exists(unsigned nodeID) const;
void addChild(unsigned parentID, unsigned newID);
void addNode(unsigned newID);
//void moveNode(unsigned nodeID, unsigned newParentID,
//bool breakCurrParentChildConn, bool breakCurrChildParentConn);
// moves children of deleted node up one in heirarchy. New connections from parent->child have weight 0.0
void deleteNode(unsigned nodeID);
/// remove all nodes but root from CPG;
void clear();
// every node in the network
void setFreqCompensation(double compensation);
void setParam_t2Overt1(double val);
void setParam_c(double val);
void setParam_b(double val);
void setParam_g(double val);
void setSampleRate(unsigned sampleRate);
bool loadWeightScalingCurve(std::string curveSource);
bool loadWeightScalingCurve(std::vector<float> x, std::vector<float> y);
void setUnityConnectionWeight(float unity);
void setConnectionWeightScaling(bool on);
void setNodeFrequency(unsigned nodeID, double freq, bool inherit);
//void setNodeFrequencyMultiple(unsigned nodeID, double multipleOfParent,
// bool inherit);
void setNodePhaseOffset(unsigned nodeID, double offset);
void setNodeSelfNoise(unsigned nodeID, double amount);
void setNodeDelayLineLength(unsigned nodeID, unsigned length);
void resetNodeChangeFlag_Params(unsigned nodeID);
void resetNodeChangeFlag_Inputs(unsigned nodeID);
void setNodeSynchMode(unsigned nodeID, MatsuNode::synchMode mode);
MatsuNode::synchMode getNodeSynchMode(unsigned nodeID);
unsigned getNextNodeID();
// protected - may be useful to limit which nodes can take external input
void setExternalInput(unsigned nodeID, double input, double weight = 1.0);
// Are we driving the root node from an external input?
void setDriven(bool driven);
// if we are driving from an external input, set that value
void setDrivingInput(float input);
// create the lookup wavetable and zero-state in case we are driving via an external phasor
void createExternalSyncResources();
///////////////////////////////////////////////////////////////////////////////
protected:
///////////////////////////////////////////////////////////////////////////////
private:
using matsuInternal = MatsuNode::matsuInternal;
std::vector<MatsuNode> _nodes;
std::vector<unsigned> _activeNodes;
unsigned _sampleRate;
ScalingCurve scaler;
float _drivingInput;
std::vector<float> _wavetable;
bool _driven{ false };
matsuoka_internals _zeroState;
/// Removes the specified node from the CPG.
/*! All inputs from the removed node to other nodes in the network
* are also removed, leaving no hanging references.
* Will not permit the root node (nodeID 0) to be removed
*/
// protected - will want to add elements to removal process if e.g. parent
// and child relationships are maintained.
bool removeNode(unsigned nodeID);
// useful separate freq and waveshape parameters for some uses
// and limit access depending on position in network
// TODO - split into components.
void setParam(unsigned nodeID, MatsuNode::matsuParam param, double val);
void setParams(unsigned nodeID, double t1, double t2, double c1,
double c2, double b, double g);
/// Removes a one-way connection between two _nodes
/*! If a reciprocal connection exists between the nodes specified, only
* the connection running in the direction specified will be removed.
* If either node specified does not exist, an exception is thrown.
*/
void disconnect(unsigned nodeFrom, unsigned nodeTo);
// UTILITY
void create(double t1, double t2, double c1, double c2, double b, double g);
void uncreate();
void getRootParams(double &t1, double &t2, double &c1,
double &c2, double &b, double &g);
void connect(unsigned nodeID_A, unsigned nodeID_B, double weightAtoB,
double weightBtoA, bool bothWays);
// added from AV_CPG
void setNodeFrequencyInherit(unsigned nodeID, double newFreq);
void setNodeFrequencyMultipleInherit(unsigned nodeID,
double multipleOfParent);
void setParam(MatsuNode::matsuParam param, double val);
unsigned calculateDelayLineLength(unsigned nodeID);
void setNodeDelayLine(unsigned nodeID, double freq);
void updateConnectionBasedOnFreq(unsigned nodeID, float oldFreq);
float wavetableLookup(float i);
};