blob: acbee565c5b8d8f2f6b0b584d4ed1edf069c54fb [file] [log] [blame]
Phil Burk0817a2b2019-01-03 14:50:18 -08001/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
Phil Burk328860f2019-11-04 18:43:40 -080018 * FlowGraph.h
Phil Burk0817a2b2019-01-03 14:50:18 -080019 *
Phil Burk328860f2019-11-04 18:43:40 -080020 * Processing node and ports that can be used in a simple data flow graph.
21 * This was designed to work with audio but could be used for other
22 * types of data.
Phil Burk0817a2b2019-01-03 14:50:18 -080023 */
24
Phil Burk328860f2019-11-04 18:43:40 -080025#ifndef FLOWGRAPH_FLOW_GRAPH_NODE_H
26#define FLOWGRAPH_FLOW_GRAPH_NODE_H
Phil Burk0817a2b2019-01-03 14:50:18 -080027
28#include <cassert>
29#include <cstring>
30#include <math.h>
Phil Burk91362012019-03-04 12:00:30 -080031#include <memory>
Phil Burk0817a2b2019-01-03 14:50:18 -080032#include <sys/types.h>
33#include <time.h>
34#include <unistd.h>
Phil Burk22f21132019-03-08 12:43:19 -080035#include <vector>
Phil Burk0817a2b2019-01-03 14:50:18 -080036
Phil Burk91362012019-03-04 12:00:30 -080037// TODO Move these classes into separate files.
Phil Burk91362012019-03-04 12:00:30 -080038// TODO Review use of raw pointers for connect(). Maybe use smart pointers but need to avoid
39// run-time deallocation in audio thread.
40
Phil Burk51ca7552019-01-04 10:59:13 -080041// Set this to 1 if using it inside the Android framework.
Phil Burk91362012019-03-04 12:00:30 -080042// This code is kept here so that it can be moved easily between Oboe and AAudio.
Phil Burk9425ffd2020-07-09 13:21:09 -070043#ifndef FLOWGRAPH_ANDROID_INTERNAL
Phil Burk0817a2b2019-01-03 14:50:18 -080044#define FLOWGRAPH_ANDROID_INTERNAL 0
Phil Burk9425ffd2020-07-09 13:21:09 -070045#endif
Phil Burk0817a2b2019-01-03 14:50:18 -080046
Phil Burk9425ffd2020-07-09 13:21:09 -070047// Set this to a name that will prevent AAudio from calling into Oboe.
Phil Burk82f422f2020-07-14 16:46:37 -070048// AAudio and Oboe both use a version of this flowgraph package.
49// There was a problem in the unit tests where AAudio would call a constructor
50// in AAudio and then call a destructor in Oboe! That caused memory corruption.
51// For more details, see Issue #930.
Phil Burk9425ffd2020-07-09 13:21:09 -070052#ifndef FLOWGRAPH_OUTER_NAMESPACE
53#define FLOWGRAPH_OUTER_NAMESPACE oboe
54#endif
55
56namespace FLOWGRAPH_OUTER_NAMESPACE {
Phil Burk0817a2b2019-01-03 14:50:18 -080057namespace flowgraph {
58
Phil Burk328860f2019-11-04 18:43:40 -080059// Default block size that can be overridden when the FlowGraphPortFloat is created.
Phil Burk0817a2b2019-01-03 14:50:18 -080060// If it is too small then we will have too much overhead from switching between nodes.
61// If it is too high then we will thrash the caches.
Phil Burk91362012019-03-04 12:00:30 -080062constexpr int kDefaultBufferSize = 8; // arbitrary
Phil Burk0817a2b2019-01-03 14:50:18 -080063
Phil Burk328860f2019-11-04 18:43:40 -080064class FlowGraphPort;
65class FlowGraphPortFloatInput;
Phil Burk0817a2b2019-01-03 14:50:18 -080066
67/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -080068/**
69 * Base class for all nodes in the flowgraph.
70 */
Phil Burk328860f2019-11-04 18:43:40 -080071class FlowGraphNode {
Phil Burk0817a2b2019-01-03 14:50:18 -080072public:
Phil Burk328860f2019-11-04 18:43:40 -080073 FlowGraphNode() {}
74 virtual ~FlowGraphNode() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -080075
76 /**
Phil Burk91362012019-03-04 12:00:30 -080077 * Read from the input ports,
78 * generate multiple frames of data then write the results to the output ports.
Phil Burk0817a2b2019-01-03 14:50:18 -080079 *
Phil Burk0817a2b2019-01-03 14:50:18 -080080 * @param numFrames maximum number of frames requested for processing
81 * @return number of frames actually processed
82 */
Phil Burk22f21132019-03-08 12:43:19 -080083 virtual int32_t onProcess(int32_t numFrames) = 0;
Phil Burk0817a2b2019-01-03 14:50:18 -080084
85 /**
Phil Burk226f4d42020-07-01 11:35:32 -070086 * If the callCount is at or after the previous callCount then call
87 * pullData on all of the upstreamNodes.
88 * Then call onProcess().
Phil Burk0817a2b2019-01-03 14:50:18 -080089 * This prevents infinite recursion in case of cyclic graphs.
90 * It also prevents nodes upstream from a branch from being executed twice.
91 *
Phil Burk226f4d42020-07-01 11:35:32 -070092 * @param callCount
Phil Burk0817a2b2019-01-03 14:50:18 -080093 * @param numFrames
94 * @return number of frames valid
95 */
Phil Burk226f4d42020-07-01 11:35:32 -070096 int32_t pullData(int32_t numFrames, int64_t callCount);
Phil Burk0817a2b2019-01-03 14:50:18 -080097
Phil Burkf67a97f2019-05-30 12:48:17 -070098 /**
99 * Recursively reset all the nodes in the graph, starting from a Sink.
Phil Burk41b868c2019-08-28 15:39:05 -0700100 *
101 * This must not be called at the same time as pullData!
Phil Burkf67a97f2019-05-30 12:48:17 -0700102 */
103 void pullReset();
104
105 /**
106 * Reset framePosition counters.
107 */
108 virtual void reset();
109
Phil Burk328860f2019-11-04 18:43:40 -0800110 void addInputPort(FlowGraphPort &port) {
Phil Burk22f21132019-03-08 12:43:19 -0800111 mInputPorts.push_back(port);
112 }
113
Phil Burkdd3971d2019-05-17 18:42:58 -0700114 bool isDataPulledAutomatically() const {
115 return mDataPulledAutomatically;
116 }
117
118 /**
119 * Set true if you want the data pulled through the graph automatically.
120 * This is the default.
121 *
Phil Burk41b868c2019-08-28 15:39:05 -0700122 * Set false if you want to pull the data from the input ports in the onProcess() method.
Phil Burkdd3971d2019-05-17 18:42:58 -0700123 * You might do this, for example, in a sample rate converting node.
124 *
125 * @param automatic
126 */
127 void setDataPulledAutomatically(bool automatic) {
128 mDataPulledAutomatically = automatic;
129 }
130
Phil Burkf67a97f2019-05-30 12:48:17 -0700131 virtual const char *getName() {
Phil Burk328860f2019-11-04 18:43:40 -0800132 return "FlowGraph";
Phil Burkf67a97f2019-05-30 12:48:17 -0700133 }
134
Phil Burk226f4d42020-07-01 11:35:32 -0700135 int64_t getLastCallCount() {
136 return mLastCallCount;
Phil Burkf67a97f2019-05-30 12:48:17 -0700137 }
138
Phil Burk0817a2b2019-01-03 14:50:18 -0800139protected:
Phil Burk226f4d42020-07-01 11:35:32 -0700140
141 static constexpr int64_t kInitialCallCount = -1;
142 int64_t mLastCallCount = kInitialCallCount;
Phil Burk0817a2b2019-01-03 14:50:18 -0800143
Phil Burk328860f2019-11-04 18:43:40 -0800144 std::vector<std::reference_wrapper<FlowGraphPort>> mInputPorts;
Phil Burk22f21132019-03-08 12:43:19 -0800145
Phil Burk0817a2b2019-01-03 14:50:18 -0800146private:
Phil Burkdd3971d2019-05-17 18:42:58 -0700147 bool mDataPulledAutomatically = true;
Phil Burkf67a97f2019-05-30 12:48:17 -0700148 bool mBlockRecursion = false;
149 int32_t mLastFrameCount = 0;
Phil Burkdd3971d2019-05-17 18:42:58 -0700150
Phil Burk0817a2b2019-01-03 14:50:18 -0800151};
152
153/***************************************************************************/
154/**
155 * This is a connector that allows data to flow between modules.
Phil Burk72356eb2019-01-09 10:35:51 -0800156 *
157 * The ports are the primary means of interacting with a module.
158 * So they are generally declared as public.
159 *
Phil Burk0817a2b2019-01-03 14:50:18 -0800160 */
Phil Burk328860f2019-11-04 18:43:40 -0800161class FlowGraphPort {
Phil Burk0817a2b2019-01-03 14:50:18 -0800162public:
Phil Burk328860f2019-11-04 18:43:40 -0800163 FlowGraphPort(FlowGraphNode &parent, int32_t samplesPerFrame)
Phil Burk2da4a842019-08-16 12:15:23 -0700164 : mContainingNode(parent)
Phil Burk0817a2b2019-01-03 14:50:18 -0800165 , mSamplesPerFrame(samplesPerFrame) {
166 }
jimmytobin2425f66d1622020-10-08 15:31:46 -0700167
168 virtual ~FlowGraphPort() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800169
170 // Ports are often declared public. So let's make them non-copyable.
Phil Burk328860f2019-11-04 18:43:40 -0800171 FlowGraphPort(const FlowGraphPort&) = delete;
172 FlowGraphPort& operator=(const FlowGraphPort&) = delete;
Phil Burk0817a2b2019-01-03 14:50:18 -0800173
174 int32_t getSamplesPerFrame() const {
175 return mSamplesPerFrame;
176 }
177
Phil Burk22f21132019-03-08 12:43:19 -0800178 virtual int32_t pullData(int64_t framePosition, int32_t numFrames) = 0;
179
Phil Burkf67a97f2019-05-30 12:48:17 -0700180 virtual void pullReset() {}
181
Phil Burk0817a2b2019-01-03 14:50:18 -0800182protected:
Phil Burk328860f2019-11-04 18:43:40 -0800183 FlowGraphNode &mContainingNode;
Phil Burk0817a2b2019-01-03 14:50:18 -0800184
185private:
186 const int32_t mSamplesPerFrame = 1;
187};
188
189/***************************************************************************/
190/**
Phil Burk91362012019-03-04 12:00:30 -0800191 * This port contains a 32-bit float buffer that can contain several frames of data.
192 * Processing the data in a block improves performance.
193 *
194 * The size is framesPerBuffer * samplesPerFrame).
Phil Burk0817a2b2019-01-03 14:50:18 -0800195 */
Phil Burk328860f2019-11-04 18:43:40 -0800196class FlowGraphPortFloat : public FlowGraphPort {
Phil Burk0817a2b2019-01-03 14:50:18 -0800197public:
Phil Burk328860f2019-11-04 18:43:40 -0800198 FlowGraphPortFloat(FlowGraphNode &parent,
Phil Burk0817a2b2019-01-03 14:50:18 -0800199 int32_t samplesPerFrame,
Phil Burk91362012019-03-04 12:00:30 -0800200 int32_t framesPerBuffer = kDefaultBufferSize
Phil Burk0817a2b2019-01-03 14:50:18 -0800201 );
202
Phil Burk328860f2019-11-04 18:43:40 -0800203 virtual ~FlowGraphPortFloat() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800204
Phil Burk91362012019-03-04 12:00:30 -0800205 int32_t getFramesPerBuffer() const {
206 return mFramesPerBuffer;
Phil Burk0817a2b2019-01-03 14:50:18 -0800207 }
208
209protected:
210
211 /**
212 * @return buffer internal to the port or from a connected port
213 */
Phil Burk91362012019-03-04 12:00:30 -0800214 virtual float *getBuffer() {
215 return mBuffer.get();
Phil Burk0817a2b2019-01-03 14:50:18 -0800216 }
217
Phil Burk0817a2b2019-01-03 14:50:18 -0800218private:
Phil Burk91362012019-03-04 12:00:30 -0800219 const int32_t mFramesPerBuffer = 1;
220 std::unique_ptr<float[]> mBuffer; // allocated in constructor
Phil Burk0817a2b2019-01-03 14:50:18 -0800221};
222
223/***************************************************************************/
224/**
Phil Burk91362012019-03-04 12:00:30 -0800225 * The results of a node's processing are stored in the buffers of the output ports.
Phil Burk0817a2b2019-01-03 14:50:18 -0800226 */
Phil Burk328860f2019-11-04 18:43:40 -0800227class FlowGraphPortFloatOutput : public FlowGraphPortFloat {
Phil Burk0817a2b2019-01-03 14:50:18 -0800228public:
Phil Burk328860f2019-11-04 18:43:40 -0800229 FlowGraphPortFloatOutput(FlowGraphNode &parent, int32_t samplesPerFrame)
230 : FlowGraphPortFloat(parent, samplesPerFrame) {
Phil Burk0817a2b2019-01-03 14:50:18 -0800231 }
232
Phil Burk328860f2019-11-04 18:43:40 -0800233 virtual ~FlowGraphPortFloatOutput() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800234
Phil Burk328860f2019-11-04 18:43:40 -0800235 using FlowGraphPortFloat::getBuffer;
Phil Burk0817a2b2019-01-03 14:50:18 -0800236
237 /**
Phil Burk0817a2b2019-01-03 14:50:18 -0800238 * Connect to the input of another module.
239 * An input port can only have one connection.
240 * An output port can have multiple connections.
241 * If you connect a second output port to an input port
242 * then it overwrites the previous connection.
243 *
Phil Burk51ca7552019-01-04 10:59:13 -0800244 * This not thread safe. Do not modify the graph topology from another thread while running.
245 * Also do not delete a module while it is connected to another port if the graph is running.
Phil Burk0817a2b2019-01-03 14:50:18 -0800246 */
Phil Burk328860f2019-11-04 18:43:40 -0800247 void connect(FlowGraphPortFloatInput *port);
Phil Burk0817a2b2019-01-03 14:50:18 -0800248
249 /**
250 * Disconnect from the input of another module.
251 * This not thread safe.
252 */
Phil Burk328860f2019-11-04 18:43:40 -0800253 void disconnect(FlowGraphPortFloatInput *port);
Phil Burk22f21132019-03-08 12:43:19 -0800254
255 /**
256 * Call the parent module's onProcess() method.
257 * That may pull data from its inputs and recursively
258 * process the entire graph.
259 * @return number of frames actually pulled
260 */
261 int32_t pullData(int64_t framePosition, int32_t numFrames) override;
262
Phil Burkf67a97f2019-05-30 12:48:17 -0700263
264 void pullReset() override;
265
Phil Burk0817a2b2019-01-03 14:50:18 -0800266};
267
268/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -0800269
270/**
271 * An input port for streaming audio data.
272 * You can set a value that will be used for processing.
273 * If you connect an output port to this port then its value will be used instead.
274 */
Phil Burk328860f2019-11-04 18:43:40 -0800275class FlowGraphPortFloatInput : public FlowGraphPortFloat {
Phil Burk0817a2b2019-01-03 14:50:18 -0800276public:
Phil Burk328860f2019-11-04 18:43:40 -0800277 FlowGraphPortFloatInput(FlowGraphNode &parent, int32_t samplesPerFrame)
278 : FlowGraphPortFloat(parent, samplesPerFrame) {
Phil Burk22f21132019-03-08 12:43:19 -0800279 // Add to parent so it can pull data from each input.
280 parent.addInputPort(*this);
Phil Burk0817a2b2019-01-03 14:50:18 -0800281 }
282
Phil Burk328860f2019-11-04 18:43:40 -0800283 virtual ~FlowGraphPortFloatInput() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800284
285 /**
286 * If connected to an output port then this will return
287 * that output ports buffers.
288 * If not connected then it returns the input ports own buffer
289 * which can be loaded using setValue().
290 */
Phil Burk91362012019-03-04 12:00:30 -0800291 float *getBuffer() override;
Phil Burk0817a2b2019-01-03 14:50:18 -0800292
293 /**
Phil Burk0817a2b2019-01-03 14:50:18 -0800294 * Write every value of the float buffer.
295 * This value will be ignored if an output port is connected
296 * to this port.
297 */
298 void setValue(float value) {
Phil Burk91362012019-03-04 12:00:30 -0800299 int numFloats = kDefaultBufferSize * getSamplesPerFrame();
300 float *buffer = getBuffer();
Phil Burk0817a2b2019-01-03 14:50:18 -0800301 for (int i = 0; i < numFloats; i++) {
302 *buffer++ = value;
303 }
304 }
305
306 /**
307 * Connect to the output of another module.
308 * An input port can only have one connection.
309 * An output port can have multiple connections.
310 * This not thread safe.
311 */
Phil Burk328860f2019-11-04 18:43:40 -0800312 void connect(FlowGraphPortFloatOutput *port) {
Phil Burk0817a2b2019-01-03 14:50:18 -0800313 assert(getSamplesPerFrame() == port->getSamplesPerFrame());
314 mConnected = port;
315 }
316
Phil Burk328860f2019-11-04 18:43:40 -0800317 void disconnect(FlowGraphPortFloatOutput *port) {
Phil Burk0817a2b2019-01-03 14:50:18 -0800318 assert(mConnected == port);
319 (void) port;
320 mConnected = nullptr;
321 }
322
323 void disconnect() {
324 mConnected = nullptr;
325 }
326
Phil Burk22f21132019-03-08 12:43:19 -0800327 /**
328 * Pull data from any output port that is connected.
329 */
330 int32_t pullData(int64_t framePosition, int32_t numFrames) override;
331
Phil Burkf67a97f2019-05-30 12:48:17 -0700332 void pullReset() override;
333
Phil Burk0817a2b2019-01-03 14:50:18 -0800334private:
Phil Burk328860f2019-11-04 18:43:40 -0800335 FlowGraphPortFloatOutput *mConnected = nullptr;
Phil Burk0817a2b2019-01-03 14:50:18 -0800336};
337
338/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -0800339
340/**
341 * Base class for an edge node in a graph that has no upstream nodes.
342 * It outputs data but does not consume data.
343 * By default, it will read its data from an external buffer.
344 */
Phil Burk328860f2019-11-04 18:43:40 -0800345class FlowGraphSource : public FlowGraphNode {
Phil Burk0817a2b2019-01-03 14:50:18 -0800346public:
Phil Burk328860f2019-11-04 18:43:40 -0800347 explicit FlowGraphSource(int32_t channelCount)
Phil Burk0817a2b2019-01-03 14:50:18 -0800348 : output(*this, channelCount) {
349 }
350
Phil Burk328860f2019-11-04 18:43:40 -0800351 virtual ~FlowGraphSource() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800352
Phil Burk328860f2019-11-04 18:43:40 -0800353 FlowGraphPortFloatOutput output;
Phil Burk561cda32019-07-11 12:40:52 -0700354};
355
356/***************************************************************************/
357
358/**
359 * Base class for an edge node in a graph that has no upstream nodes.
360 * It outputs data but does not consume data.
361 * By default, it will read its data from an external buffer.
362 */
Phil Burk328860f2019-11-04 18:43:40 -0800363class FlowGraphSourceBuffered : public FlowGraphSource {
Phil Burk561cda32019-07-11 12:40:52 -0700364public:
Phil Burk328860f2019-11-04 18:43:40 -0800365 explicit FlowGraphSourceBuffered(int32_t channelCount)
366 : FlowGraphSource(channelCount) {}
Phil Burk561cda32019-07-11 12:40:52 -0700367
Phil Burk328860f2019-11-04 18:43:40 -0800368 virtual ~FlowGraphSourceBuffered() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800369
Phil Burk91362012019-03-04 12:00:30 -0800370 /**
371 * Specify buffer that the node will read from.
372 *
373 * @param data TODO Consider using std::shared_ptr.
374 * @param numFrames
375 */
Phil Burk0817a2b2019-01-03 14:50:18 -0800376 void setData(const void *data, int32_t numFrames) {
377 mData = data;
378 mSizeInFrames = numFrames;
379 mFrameIndex = 0;
380 }
381
382protected:
383 const void *mData = nullptr;
384 int32_t mSizeInFrames = 0; // number of frames in mData
385 int32_t mFrameIndex = 0; // index of next frame to be processed
386};
387
388/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -0800389/**
390 * Base class for an edge node in a graph that has no downstream nodes.
391 * It consumes data but does not output data.
392 * This graph will be executed when data is read() from this node
393 * by pulling data from upstream nodes.
394 */
Phil Burk328860f2019-11-04 18:43:40 -0800395class FlowGraphSink : public FlowGraphNode {
Phil Burk0817a2b2019-01-03 14:50:18 -0800396public:
Phil Burk328860f2019-11-04 18:43:40 -0800397 explicit FlowGraphSink(int32_t channelCount)
Phil Burk0817a2b2019-01-03 14:50:18 -0800398 : input(*this, channelCount) {
399 }
400
Phil Burk328860f2019-11-04 18:43:40 -0800401 virtual ~FlowGraphSink() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800402
Phil Burk328860f2019-11-04 18:43:40 -0800403 FlowGraphPortFloatInput input;
Phil Burk0817a2b2019-01-03 14:50:18 -0800404
405 /**
406 * Dummy processor. The work happens in the read() method.
407 *
Phil Burk0817a2b2019-01-03 14:50:18 -0800408 * @param numFrames
409 * @return number of frames actually processed
410 */
Phil Burk22f21132019-03-08 12:43:19 -0800411 int32_t onProcess(int32_t numFrames) override {
412 return numFrames;
Phil Burk84e7e652019-05-13 16:29:15 -0700413 }
Phil Burk0817a2b2019-01-03 14:50:18 -0800414
Phil Burk226f4d42020-07-01 11:35:32 -0700415 virtual int32_t read(void *data, int32_t numFrames) = 0;
Phil Burk0817a2b2019-01-03 14:50:18 -0800416
Phil Burk226f4d42020-07-01 11:35:32 -0700417protected:
418 /**
419 * Pull data through the graph using this nodes last callCount.
420 * @param numFrames
421 * @return
422 */
423 int32_t pullData(int32_t numFrames);
Phil Burk0817a2b2019-01-03 14:50:18 -0800424};
425
Phil Burkf5b3b812019-06-04 07:51:07 -0700426/***************************************************************************/
427/**
Phil Burk2da4a842019-08-16 12:15:23 -0700428 * Base class for a node that has an input and an output with the same number of channels.
Phil Burkf5b3b812019-06-04 07:51:07 -0700429 * This may include traditional filters, eg. FIR, but also include
430 * any processing node that converts input to output.
431 */
Phil Burk328860f2019-11-04 18:43:40 -0800432class FlowGraphFilter : public FlowGraphNode {
Phil Burkf5b3b812019-06-04 07:51:07 -0700433public:
Phil Burk328860f2019-11-04 18:43:40 -0800434 explicit FlowGraphFilter(int32_t channelCount)
Phil Burkf5b3b812019-06-04 07:51:07 -0700435 : input(*this, channelCount)
436 , output(*this, channelCount) {
437 }
438
Phil Burk328860f2019-11-04 18:43:40 -0800439 virtual ~FlowGraphFilter() = default;
Phil Burkf5b3b812019-06-04 07:51:07 -0700440
Phil Burk328860f2019-11-04 18:43:40 -0800441 FlowGraphPortFloatInput input;
442 FlowGraphPortFloatOutput output;
Phil Burkf5b3b812019-06-04 07:51:07 -0700443};
444
Phil Burk0817a2b2019-01-03 14:50:18 -0800445} /* namespace flowgraph */
Phil Burk2c8f2812020-07-13 10:55:04 -0700446} /* namespace FLOWGRAPH_OUTER_NAMESPACE */
Phil Burk0817a2b2019-01-03 14:50:18 -0800447
Phil Burk328860f2019-11-04 18:43:40 -0800448#endif /* FLOWGRAPH_FLOW_GRAPH_NODE_H */