aboutsummaryrefslogtreecommitdiff
path: root/externals/oboe/src/flowgraph/FlowGraphNode.h
diff options
context:
space:
mode:
authorswurl <swurl@swurl.xyz>2025-05-31 02:33:02 -0400
committerswurl <swurl@swurl.xyz>2025-05-31 02:33:02 -0400
commit6c655321e6b04b8648802e296bb5ca11d290ae68 (patch)
tree5c906511e4a2910ce8b806bb9d763b524c28619e /externals/oboe/src/flowgraph/FlowGraphNode.h
parentc0cceff365f754f8859f4c9a0362ebdba7ea388b (diff)
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
Diffstat (limited to 'externals/oboe/src/flowgraph/FlowGraphNode.h')
-rw-r--r--externals/oboe/src/flowgraph/FlowGraphNode.h450
1 files changed, 450 insertions, 0 deletions
diff --git a/externals/oboe/src/flowgraph/FlowGraphNode.h b/externals/oboe/src/flowgraph/FlowGraphNode.h
new file mode 100644
index 0000000000..2884c081fb
--- /dev/null
+++ b/externals/oboe/src/flowgraph/FlowGraphNode.h
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * FlowGraph.h
+ *
+ * Processing node and ports that can be used in a simple data flow graph.
+ * This was designed to work with audio but could be used for other
+ * types of data.
+ */
+
+#ifndef FLOWGRAPH_FLOW_GRAPH_NODE_H
+#define FLOWGRAPH_FLOW_GRAPH_NODE_H
+
+#include <cassert>
+#include <cstring>
+#include <math.h>
+#include <memory>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <vector>
+
+// TODO Move these classes into separate files.
+// TODO Review use of raw pointers for connect(). Maybe use smart pointers but need to avoid
+// run-time deallocation in audio thread.
+
+// Set flags FLOWGRAPH_ANDROID_INTERNAL and FLOWGRAPH_OUTER_NAMESPACE based on whether compiler
+// flag __ANDROID_NDK__ is defined. __ANDROID_NDK__ should be defined in oboe and not aaudio.
+
+#ifndef FLOWGRAPH_ANDROID_INTERNAL
+#ifdef __ANDROID_NDK__
+#define FLOWGRAPH_ANDROID_INTERNAL 0
+#else
+#define FLOWGRAPH_ANDROID_INTERNAL 1
+#endif // __ANDROID_NDK__
+#endif // FLOWGRAPH_ANDROID_INTERNAL
+
+#ifndef FLOWGRAPH_OUTER_NAMESPACE
+#ifdef __ANDROID_NDK__
+#define FLOWGRAPH_OUTER_NAMESPACE oboe
+#else
+#define FLOWGRAPH_OUTER_NAMESPACE aaudio
+#endif // __ANDROID_NDK__
+#endif // FLOWGRAPH_OUTER_NAMESPACE
+
+namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph {
+
+// Default block size that can be overridden when the FlowGraphPortFloat is created.
+// If it is too small then we will have too much overhead from switching between nodes.
+// If it is too high then we will thrash the caches.
+constexpr int kDefaultBufferSize = 8; // arbitrary
+
+class FlowGraphPort;
+class FlowGraphPortFloatInput;
+
+/***************************************************************************/
+/**
+ * Base class for all nodes in the flowgraph.
+ */
+class FlowGraphNode {
+public:
+ FlowGraphNode() = default;
+ virtual ~FlowGraphNode() = default;
+
+ /**
+ * Read from the input ports,
+ * generate multiple frames of data then write the results to the output ports.
+ *
+ * @param numFrames maximum number of frames requested for processing
+ * @return number of frames actually processed
+ */
+ virtual int32_t onProcess(int32_t numFrames) = 0;
+
+ /**
+ * If the callCount is at or after the previous callCount then call
+ * pullData on all of the upstreamNodes.
+ * Then call onProcess().
+ * This prevents infinite recursion in case of cyclic graphs.
+ * It also prevents nodes upstream from a branch from being executed twice.
+ *
+ * @param callCount
+ * @param numFrames
+ * @return number of frames valid
+ */
+ int32_t pullData(int32_t numFrames, int64_t callCount);
+
+ /**
+ * Recursively reset all the nodes in the graph, starting from a Sink.
+ *
+ * This must not be called at the same time as pullData!
+ */
+ void pullReset();
+
+ /**
+ * Reset framePosition counters.
+ */
+ virtual void reset();
+
+ void addInputPort(FlowGraphPort &port) {
+ mInputPorts.emplace_back(port);
+ }
+
+ bool isDataPulledAutomatically() const {
+ return mDataPulledAutomatically;
+ }
+
+ /**
+ * Set true if you want the data pulled through the graph automatically.
+ * This is the default.
+ *
+ * Set false if you want to pull the data from the input ports in the onProcess() method.
+ * You might do this, for example, in a sample rate converting node.
+ *
+ * @param automatic
+ */
+ void setDataPulledAutomatically(bool automatic) {
+ mDataPulledAutomatically = automatic;
+ }
+
+ virtual const char *getName() {
+ return "FlowGraph";
+ }
+
+ int64_t getLastCallCount() {
+ return mLastCallCount;
+ }
+
+protected:
+
+ static constexpr int64_t kInitialCallCount = -1;
+ int64_t mLastCallCount = kInitialCallCount;
+
+ std::vector<std::reference_wrapper<FlowGraphPort>> mInputPorts;
+
+private:
+ bool mDataPulledAutomatically = true;
+ bool mBlockRecursion = false;
+ int32_t mLastFrameCount = 0;
+
+};
+
+/***************************************************************************/
+/**
+ * This is a connector that allows data to flow between modules.
+ *
+ * The ports are the primary means of interacting with a module.
+ * So they are generally declared as public.
+ *
+ */
+class FlowGraphPort {
+public:
+ FlowGraphPort(FlowGraphNode &parent, int32_t samplesPerFrame)
+ : mContainingNode(parent)
+ , mSamplesPerFrame(samplesPerFrame) {
+ }
+
+ virtual ~FlowGraphPort() = default;
+
+ // Ports are often declared public. So let's make them non-copyable.
+ FlowGraphPort(const FlowGraphPort&) = delete;
+ FlowGraphPort& operator=(const FlowGraphPort&) = delete;
+
+ int32_t getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+ virtual int32_t pullData(int64_t framePosition, int32_t numFrames) = 0;
+
+ virtual void pullReset() {}
+
+protected:
+ FlowGraphNode &mContainingNode;
+
+private:
+ const int32_t mSamplesPerFrame = 1;
+};
+
+/***************************************************************************/
+/**
+ * This port contains a 32-bit float buffer that can contain several frames of data.
+ * Processing the data in a block improves performance.
+ *
+ * The size is framesPerBuffer * samplesPerFrame).
+ */
+class FlowGraphPortFloat : public FlowGraphPort {
+public:
+ FlowGraphPortFloat(FlowGraphNode &parent,
+ int32_t samplesPerFrame,
+ int32_t framesPerBuffer = kDefaultBufferSize
+ );
+
+ virtual ~FlowGraphPortFloat() = default;
+
+ int32_t getFramesPerBuffer() const {
+ return mFramesPerBuffer;
+ }
+
+protected:
+
+ /**
+ * @return buffer internal to the port or from a connected port
+ */
+ virtual float *getBuffer() {
+ return mBuffer.get();
+ }
+
+private:
+ const int32_t mFramesPerBuffer = 1;
+ std::unique_ptr<float[]> mBuffer; // allocated in constructor
+};
+
+/***************************************************************************/
+/**
+ * The results of a node's processing are stored in the buffers of the output ports.
+ */
+class FlowGraphPortFloatOutput : public FlowGraphPortFloat {
+public:
+ FlowGraphPortFloatOutput(FlowGraphNode &parent, int32_t samplesPerFrame)
+ : FlowGraphPortFloat(parent, samplesPerFrame) {
+ }
+
+ virtual ~FlowGraphPortFloatOutput() = default;
+
+ using FlowGraphPortFloat::getBuffer;
+
+ /**
+ * Connect to the input of another module.
+ * An input port can only have one connection.
+ * An output port can have multiple connections.
+ * If you connect a second output port to an input port
+ * then it overwrites the previous connection.
+ *
+ * This not thread safe. Do not modify the graph topology from another thread while running.
+ * Also do not delete a module while it is connected to another port if the graph is running.
+ */
+ void connect(FlowGraphPortFloatInput *port);
+
+ /**
+ * Disconnect from the input of another module.
+ * This not thread safe.
+ */
+ void disconnect(FlowGraphPortFloatInput *port);
+
+ /**
+ * Call the parent module's onProcess() method.
+ * That may pull data from its inputs and recursively
+ * process the entire graph.
+ * @return number of frames actually pulled
+ */
+ int32_t pullData(int64_t framePosition, int32_t numFrames) override;
+
+
+ void pullReset() override;
+
+};
+
+/***************************************************************************/
+
+/**
+ * An input port for streaming audio data.
+ * You can set a value that will be used for processing.
+ * If you connect an output port to this port then its value will be used instead.
+ */
+class FlowGraphPortFloatInput : public FlowGraphPortFloat {
+public:
+ FlowGraphPortFloatInput(FlowGraphNode &parent, int32_t samplesPerFrame)
+ : FlowGraphPortFloat(parent, samplesPerFrame) {
+ // Add to parent so it can pull data from each input.
+ parent.addInputPort(*this);
+ }
+
+ virtual ~FlowGraphPortFloatInput() = default;
+
+ /**
+ * If connected to an output port then this will return
+ * that output ports buffers.
+ * If not connected then it returns the input ports own buffer
+ * which can be loaded using setValue().
+ */
+ float *getBuffer() override;
+
+ /**
+ * Write every value of the float buffer.
+ * This value will be ignored if an output port is connected
+ * to this port.
+ */
+ void setValue(float value) {
+ int numFloats = kDefaultBufferSize * getSamplesPerFrame();
+ float *buffer = getBuffer();
+ for (int i = 0; i < numFloats; i++) {
+ *buffer++ = value;
+ }
+ }
+
+ /**
+ * Connect to the output of another module.
+ * An input port can only have one connection.
+ * An output port can have multiple connections.
+ * This not thread safe.
+ */
+ void connect(FlowGraphPortFloatOutput *port) {
+ assert(getSamplesPerFrame() == port->getSamplesPerFrame());
+ mConnected = port;
+ }
+
+ void disconnect(FlowGraphPortFloatOutput *port) {
+ assert(mConnected == port);
+ (void) port;
+ mConnected = nullptr;
+ }
+
+ void disconnect() {
+ mConnected = nullptr;
+ }
+
+ /**
+ * Pull data from any output port that is connected.
+ */
+ int32_t pullData(int64_t framePosition, int32_t numFrames) override;
+
+ void pullReset() override;
+
+private:
+ FlowGraphPortFloatOutput *mConnected = nullptr;
+};
+
+/***************************************************************************/
+
+/**
+ * Base class for an edge node in a graph that has no upstream nodes.
+ * It outputs data but does not consume data.
+ * By default, it will read its data from an external buffer.
+ */
+class FlowGraphSource : public FlowGraphNode {
+public:
+ explicit FlowGraphSource(int32_t channelCount)
+ : output(*this, channelCount) {
+ }
+
+ virtual ~FlowGraphSource() = default;
+
+ FlowGraphPortFloatOutput output;
+};
+
+/***************************************************************************/
+
+/**
+ * Base class for an edge node in a graph that has no upstream nodes.
+ * It outputs data but does not consume data.
+ * By default, it will read its data from an external buffer.
+ */
+class FlowGraphSourceBuffered : public FlowGraphSource {
+public:
+ explicit FlowGraphSourceBuffered(int32_t channelCount)
+ : FlowGraphSource(channelCount) {}
+
+ virtual ~FlowGraphSourceBuffered() = default;
+
+ /**
+ * Specify buffer that the node will read from.
+ *
+ * @param data TODO Consider using std::shared_ptr.
+ * @param numFrames
+ */
+ void setData(const void *data, int32_t numFrames) {
+ mData = data;
+ mSizeInFrames = numFrames;
+ mFrameIndex = 0;
+ }
+
+protected:
+ const void *mData = nullptr;
+ int32_t mSizeInFrames = 0; // number of frames in mData
+ int32_t mFrameIndex = 0; // index of next frame to be processed
+};
+
+/***************************************************************************/
+/**
+ * Base class for an edge node in a graph that has no downstream nodes.
+ * It consumes data but does not output data.
+ * This graph will be executed when data is read() from this node
+ * by pulling data from upstream nodes.
+ */
+class FlowGraphSink : public FlowGraphNode {
+public:
+ explicit FlowGraphSink(int32_t channelCount)
+ : input(*this, channelCount) {
+ }
+
+ virtual ~FlowGraphSink() = default;
+
+ FlowGraphPortFloatInput input;
+
+ /**
+ * Do nothing. The work happens in the read() method.
+ *
+ * @param numFrames
+ * @return number of frames actually processed
+ */
+ int32_t onProcess(int32_t numFrames) override {
+ return numFrames;
+ }
+
+ virtual int32_t read(void *data, int32_t numFrames) = 0;
+
+protected:
+ /**
+ * Pull data through the graph using this nodes last callCount.
+ * @param numFrames
+ * @return
+ */
+ int32_t pullData(int32_t numFrames);
+};
+
+/***************************************************************************/
+/**
+ * Base class for a node that has an input and an output with the same number of channels.
+ * This may include traditional filters, eg. FIR, but also include
+ * any processing node that converts input to output.
+ */
+class FlowGraphFilter : public FlowGraphNode {
+public:
+ explicit FlowGraphFilter(int32_t channelCount)
+ : input(*this, channelCount)
+ , output(*this, channelCount) {
+ }
+
+ virtual ~FlowGraphFilter() = default;
+
+ FlowGraphPortFloatInput input;
+ FlowGraphPortFloatOutput output;
+};
+
+} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */
+
+#endif /* FLOWGRAPH_FLOW_GRAPH_NODE_H */