summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/resonance-audio/resonance_audio/node
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/resonance-audio/resonance_audio/node')
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc407
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/node.h258
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc330
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc89
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h115
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h51
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc54
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h59
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc41
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/source_node.h68
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h48
11 files changed, 1520 insertions, 0 deletions
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc b/src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc
new file mode 100644
index 000000000..a623f5d1c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc
@@ -0,0 +1,407 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#include <algorithm>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+#include "node/processing_node.h"
+#include "node/sink_node.h"
+#include "node/source_node.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of channels in test buffers.
+static const size_t kNumChannels = 5;
+
+// Number of frames in test buffers.
+static const size_t kNumFrames = 7;
+
+// Helper method to compare two audio buffers.
+bool CompareAudioBuffer(const AudioBuffer& buffer_a,
+ const AudioBuffer& buffer_b) {
+ if (buffer_a.num_channels() != buffer_b.num_channels() ||
+ buffer_a.num_frames() != buffer_b.num_frames()) {
+ return false;
+ }
+ for (size_t channel = 0; channel < buffer_a.num_channels(); ++channel) {
+ const AudioBuffer::Channel& channel_a = buffer_a[channel];
+ const AudioBuffer::Channel& channel_b = buffer_b[channel];
+ for (size_t frame = 0; frame < buffer_a.num_frames(); ++frame) {
+ if (channel_a[frame] != channel_b[frame]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Helper method to generate a test AudioBuffer.
+std::unique_ptr<AudioBuffer> GenerateTestAudioBuffer(float factor) {
+ std::unique_ptr<AudioBuffer> new_buffer(
+ new AudioBuffer(kNumChannels, kNumFrames));
+ for (size_t channel = 0; channel < kNumChannels; ++channel) {
+ std::fill((*new_buffer)[channel].begin(), (*new_buffer)[channel].end(),
+ static_cast<float>(channel) * factor);
+ }
+ return new_buffer;
+}
+
+// Simple audio source node that generates AudioData buffers.
+class MySourceNode : public SourceNode {
+ public:
+ MySourceNode(bool output_empty_buffer, bool* node_deletion_flag)
+ : output_empty_buffer_(output_empty_buffer),
+ node_deletion_flag_(node_deletion_flag),
+ audio_buffer_(GenerateTestAudioBuffer(1.0f)) {}
+ ~MySourceNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ protected:
+ // AudioProcess methods outputs kTestAudioData data.
+ const AudioBuffer* AudioProcess() override {
+ if (output_empty_buffer_) {
+ return nullptr;
+ }
+ return audio_buffer_.get();
+ }
+
+ private:
+ const bool output_empty_buffer_;
+ bool* node_deletion_flag_;
+
+ std::unique_ptr<AudioBuffer> audio_buffer_;
+};
+
+// Simple audio processing node that reads from a single input and passes the
+// data to the output.
+class MyProcessingNode : public ProcessingNode {
+ public:
+ MyProcessingNode(bool process_on_empty_input, bool* audio_process_called_flag,
+ bool* node_deletion_flag)
+ : process_on_empty_input_(process_on_empty_input),
+ audio_process_called_flag_(audio_process_called_flag),
+ node_deletion_flag_(node_deletion_flag) {
+ EnableProcessOnEmptyInput(process_on_empty_input_);
+ }
+
+ ~MyProcessingNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ protected:
+ const AudioBuffer* AudioProcess(const NodeInput& input) override {
+ if (audio_process_called_flag_ != nullptr) {
+ *audio_process_called_flag_ = true;
+ }
+
+ if (!process_on_empty_input_) {
+ EXPECT_GT(input.GetInputBuffers().size(), 0U);
+ } else {
+ if (input.GetInputBuffers().empty()) {
+ return nullptr;
+ }
+ }
+
+ return input.GetInputBuffers()[0];
+ }
+
+ private:
+ const bool process_on_empty_input_;
+ bool* const audio_process_called_flag_;
+ bool* const node_deletion_flag_;
+};
+
+// Simple audio mixer node that connects to multiple nodes and outputs the sum
+// of all inputs.
+class MyAudioMixerNode : public ProcessingNode {
+ public:
+ explicit MyAudioMixerNode(bool* node_deletion_flag)
+ : node_deletion_flag_(node_deletion_flag) {}
+ ~MyAudioMixerNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ protected:
+ // AudioProcess performs an element-wise sum from all inputs and outputs a new
+ // AudioData buffer.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override {
+ const auto& input_buffers = input.GetInputBuffers();
+ output_data_ = *input_buffers[0];
+
+ // Iterate over all inputs and add its data to |output_data|.
+ for (size_t buffer = 1; buffer < input_buffers.size(); ++buffer) {
+ const AudioBuffer* accumulate_data = input_buffers[buffer];
+ for (size_t channel = 0; channel < accumulate_data->num_channels();
+ ++channel) {
+ const AudioBuffer::Channel& accumulate_channel =
+ (*accumulate_data)[channel];
+ AudioBuffer::Channel* output_channel = &output_data_[channel];
+ EXPECT_EQ(accumulate_channel.size(), output_channel->size());
+ for (size_t frame = 0; frame < accumulate_channel.size(); ++frame) {
+ (*output_channel)[frame] += accumulate_channel[frame];
+ }
+ }
+ }
+ return &output_data_;
+ }
+
+ private:
+ bool* node_deletion_flag_;
+ AudioBuffer output_data_;
+};
+
+// Simple audio sink node that expects a single input.
+class MySinkNode : public SinkNode {
+ public:
+ explicit MySinkNode(bool* node_deletion_flag)
+ : node_deletion_flag_(node_deletion_flag) {}
+
+ ~MySinkNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ private:
+ bool* node_deletion_flag_;
+};
+
+// Tests a chain of an |SinkNode|, |ProcessingNode| and
+// |SourceNode|.
+TEST(AudioNodesTest, SourceProcessingSinkConnectionTest) {
+ static const bool kOutputEmptyBuffer = false;
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ auto source_node =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, nullptr, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ // Test output data.
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_GT(data.size(), 0U);
+ EXPECT_TRUE(CompareAudioBuffer(*data[0], *GenerateTestAudioBuffer(1.0f)));
+}
+
+// Tests a chain of an |SinkNode| and |AudioMixerNode| connected to two
+// |SourceNodes|.
+TEST(AudioNodesTest, MixerProcessingConnectionTest) {
+ static const bool kOutputEmptyBuffer = false;
+ auto source_node_a =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto source_node_b =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto mixer_node = std::make_shared<MyAudioMixerNode>(nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(mixer_node);
+ mixer_node->Connect(source_node_a);
+ mixer_node->Connect(source_node_b);
+
+ // Test output data.
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_GT(data.size(), 0U);
+ EXPECT_TRUE(CompareAudioBuffer(*data[0], *GenerateTestAudioBuffer(2.0f)));
+}
+
+// Tests if ProcessingNode::AudioProcess() calls are skipped in case of
+// empty input buffers.
+TEST(AudioNodesTest, SkipProcessingOnEmptyInputTest) {
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ bool audio_process_called = false;
+
+ // Tests that ProcessingNode::AudioProcess() is called in case source
+ // nodes do generate output.
+ {
+ static const bool kOutputEmptyBuffer = false;
+ auto source_node_a =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto source_node_b =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, &audio_process_called, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node_a);
+ processing_node->Connect(source_node_b);
+
+ EXPECT_FALSE(audio_process_called);
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_TRUE(audio_process_called);
+ EXPECT_GT(data.size(), 0U);
+ }
+
+ audio_process_called = false;
+
+ // Tests that ProcessingNode::AudioProcess() is *not* called in case
+ // source nodes do *not* generate output.
+ {
+ static const bool kOutputEmptyBuffer = true;
+ auto source_node_a =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto source_node_b =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ false, &audio_process_called, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node_a);
+ processing_node->Connect(source_node_b);
+
+ EXPECT_FALSE(audio_process_called);
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_FALSE(audio_process_called);
+ EXPECT_EQ(data.size(), 0U);
+ }
+}
+
+// Tests a chain of an |SinkNode|, |ProcessingNode| and
+// |SourceNode| and runs the node clean-up procedure with sources being
+// *not* marked with end-of-stream.
+TEST(AudioNodesTest, NodeCleanUpWithoutMarkEndOfStreamCallTest) {
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ bool source_node_deleted = false;
+ bool processing_node_deleted = false;
+ bool sink_node_deleted = false;
+
+ auto sink_node = std::make_shared<MySinkNode>(&sink_node_deleted);
+
+ {
+ // Create a source and processing node and connect it to sink node.
+ auto source_node = std::make_shared<MySourceNode>(
+ /*output_empty_buffer=*/false, &source_node_deleted);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, nullptr, &processing_node_deleted);
+
+ // Connect nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ // End-of-stream is not marked in source node.
+ // source_node->MarkEndOfStream();
+ }
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+
+ sink_node->CleanUp();
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+}
+
+// Tests a chain of an SinkNode, ProcessingNode and SourceNode
+// and runs the node clean-up procedure with sources being marked with
+// end-of-stream.
+TEST(AudioNodesTest, NodeCleanUpTest) {
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ bool source_node_deleted = false;
+ bool processing_node_deleted = false;
+ bool sink_node_deleted = false;
+
+ auto sink_node = std::make_shared<MySinkNode>(&sink_node_deleted);
+
+ {
+ // Create a source and processing node and connect it to sink node.
+ auto source_node = std::make_shared<MySourceNode>(
+ /*output_empty_buffer=*/false, &source_node_deleted);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, nullptr, &processing_node_deleted);
+
+ // Connect nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ // End of stream is marked in source node. Do not expect any data anymore.
+ source_node->MarkEndOfStream();
+ }
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+
+ sink_node->CleanUp();
+
+ EXPECT_TRUE(source_node_deleted);
+ EXPECT_TRUE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+}
+
+// Tests ProcessingNode::EnableProcessOnEmptyInput().
+TEST(AudioNodesTest, ProcessOnEmptyInputFlagTest) {
+ bool audio_process_called = false;
+
+ static const bool kEnableProcessOnEmptyInput = true;
+ static const bool kOutputEmptyBuffer = true;
+
+ auto source_node =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, &audio_process_called, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ EXPECT_FALSE(audio_process_called);
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_TRUE(audio_process_called);
+ EXPECT_EQ(data.size(), 0U);
+}
+
+// Tests a chain of an |SinkNode| and |AudioMixerNode| without a |SourceNode|.
+TEST(AudioNodesTest, MissingSourceConnectionTest) {
+ auto mixer_node = std::make_shared<MyAudioMixerNode>(nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(mixer_node);
+
+ // Test output data.
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_EQ(data.size(), 0U);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/node.h b/src/3rdparty/resonance-audio/resonance_audio/node/node.h
new file mode 100644
index 000000000..e6e10e39d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/node.h
@@ -0,0 +1,258 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_NODE_H_
+#define RESONANCE_AUDIO_NODE_NODE_H_
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Implements a processing node in a synchronous processing graph.
+// This processing graph is expected to be directed, acyclic, and
+// accessed from a single thread.
+//
+// Subclasses are expected to implement Process(), which will read
+// from all of the instance's inputs, process the data as necessary,
+// and then write to all of its outputs.
+//
+// Data is passed through unique_ptrs, so nodes are expected to
+// modify data in place whenever it suits their purposes. If an
+// outputs is connected to more than one input, copies will be made for
+// each input.
+//
+// Graphs are managed through shared_ptrs. Orphaned nodes are kept
+// alive as long as they output to a living input. Ownership is
+// unidirectional -- from input to output -- in order to avoid
+// circular dependencies.
+class Node : public std::enable_shared_from_this<Node> {
+ public:
+ virtual ~Node() {}
+ virtual void Process() = 0;
+
+ // Disconnects from input nodes that are marked to be at the end of data
+ // stream.
+ //
+ // @return True if node is does not have any inputs and can be removed, false
+ // otherwise.
+ virtual bool CleanUp() = 0;
+
+ template <class T>
+ class Output;
+
+ // An endpoint for a node, this object consumes data from any connected
+ // outputs. Because an input may be connected to more than one output, it
+ // returns a vector of read data. All outputs must be of the same type.
+ template <typename T>
+ class Input {
+ public:
+ // Unordered map that stores pairs of input Node instances and their
+ // |Output| member.
+ typedef std::unordered_map<Output<T>*, std::shared_ptr<Node>> OutputNodeMap;
+
+ Input() {}
+ ~Input();
+
+ // Returns a vector of computed data, one for each connected output.
+ const std::vector<T>& Read();
+
+ // Connects this input to the specified output.
+ //
+ // @param node The parent of the output.
+ // @param output The output to connect to.
+ void Connect(const std::shared_ptr<Node>& node, Output<T>* output);
+
+ // Disconnects this input from the specified output.
+ //
+ // @param output The output to be disconnected.
+ void Disconnect(Output<T>* output);
+
+ // Returns the number of connected outputs.
+ //
+ // @return Number of connected outputs.
+ size_t GetNumConnections() const;
+
+ // Returns reference to OutputNodeMap map to obtain all connected nodes and
+ // their outputs.
+ const OutputNodeMap& GetConnectedNodeOutputPairs();
+
+ // Disable copy constructor.
+ Input(const Input& that) = delete;
+
+ private:
+ friend class Node::Output<T>;
+
+ void AddOutput(const std::shared_ptr<Node>& node, Output<T>* output);
+ void RemoveOutput(Output<T>* output);
+
+ OutputNodeMap outputs_;
+ std::vector<T> read_data_;
+ };
+
+ // An endpoint for a node, this object produces data for any connected inputs.
+ // Because an output may have more than one input, this object will duplicate
+ // any computed data, once for each connected input. All inputs must be of the
+ // same type.
+ //
+ // If an output does not have any data to deliver, it will ask its parent node
+ // to process more data. It is assumed that after processing, some new data
+ // will be written to this output.
+ template <typename T>
+ class Output {
+ public:
+ explicit Output(Node* node) : parent_(node) {}
+
+ // Parent nodes should call this function to push new data to any connected
+ // inputs. This data will be copied once for each connected input.
+ //
+ // @param data New data to pass to all connected inputs.
+ void Write(T data);
+
+ // Disable copy constructor.
+ Output(const Output& that) = delete;
+
+ private:
+ friend class Node::Input<T>;
+
+ // Signature of copy operator.
+ typedef T (*CopyOperator)(const T&);
+
+ // Returns a single piece of stored processed data. If no data exists,
+ // the parent node is processed to produce more data.
+ T PullData();
+
+ void AddInput(Input<T>* input);
+ bool RemoveInput(Input<T>* input);
+
+ std::set<Input<T>*> inputs_;
+ std::vector<T> written_data_;
+ Node* parent_;
+ };
+};
+
+template <class T>
+Node::Input<T>::~Input() {
+ for (auto& o : outputs_) {
+ CHECK(o.first->RemoveInput(this));
+ }
+}
+
+template <class T>
+const std::vector<T>& Node::Input<T>::Read() {
+ read_data_.clear();
+
+ for (auto& o : outputs_) {
+ // Obtain processed data.
+ T processed_data = o.first->PullData();
+ if (processed_data != nullptr) {
+ read_data_.emplace_back(std::move(processed_data));
+ }
+ }
+
+ return read_data_;
+}
+
+template <class T>
+void Node::Input<T>::Connect(const std::shared_ptr<Node>& node,
+ Output<T>* output) {
+ output->AddInput(this);
+ AddOutput(node, output);
+}
+
+// RemoveOutput(output) may trigger *output be destructed,
+// so we need to call output->RemoveInput(this) first.
+template <class T>
+void Node::Input<T>::Disconnect(Output<T>* output) {
+ output->RemoveInput(this);
+ RemoveOutput(output);
+}
+
+template <class T>
+size_t Node::Input<T>::GetNumConnections() const {
+ return outputs_.size();
+}
+
+template <class T>
+const typename Node::Input<T>::OutputNodeMap&
+Node::Input<T>::GetConnectedNodeOutputPairs() {
+ return outputs_;
+}
+
+template <class T>
+void Node::Input<T>::AddOutput(const std::shared_ptr<Node>& node,
+ Output<T>* output) {
+ outputs_[output] = node;
+
+ DCHECK(outputs_.find(output) != outputs_.end());
+}
+
+template <class T>
+void Node::Input<T>::RemoveOutput(Output<T>* output) {
+ outputs_.erase(output);
+}
+
+template <class T>
+T Node::Output<T>::PullData() {
+ if (written_data_.empty()) {
+ parent_->Process();
+ }
+
+ DCHECK(!written_data_.empty());
+
+ T return_value = std::move(written_data_.back());
+ written_data_.pop_back();
+ return return_value;
+}
+
+template <class T>
+void Node::Output<T>::Write(T data) {
+ DCHECK(written_data_.empty());
+ written_data_.clear();
+ written_data_.emplace_back(std::move(data));
+
+ // If we have more than one connected input, copy the data for each input.
+ for (size_t i = 1; i < inputs_.size(); i++) {
+ written_data_.push_back(written_data_[0]);
+ }
+
+ DCHECK(written_data_.size() == inputs_.size());
+}
+
+template <class T>
+void Node::Output<T>::AddInput(Input<T>* input) {
+ inputs_.insert(input);
+}
+
+template <class T>
+bool Node::Output<T>::RemoveInput(Input<T>* input) {
+ auto it = inputs_.find(input);
+ if (it == inputs_.end()) {
+ return false;
+ }
+
+ inputs_.erase(it);
+ return true;
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc
new file mode 100644
index 000000000..e95a68a1c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc
@@ -0,0 +1,330 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#include "node/node.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace vraudio {
+
+namespace {
+
+class SourceNode : public Node {
+ public:
+ explicit SourceNode(bool output_nullptr)
+ : output_nullptr_(output_nullptr), output_(this), next_value_(0) {}
+
+ void Process() final {
+ if (output_nullptr_) {
+ // Output nullptr.
+ output_.Write(nullptr);
+ } else {
+ output_.Write(&next_value_);
+ next_value_++;
+ }
+ }
+
+ bool CleanUp() final { return false; }
+
+ const bool output_nullptr_;
+ Node::Output<int*> output_;
+
+ private:
+ int next_value_;
+};
+
+class PassThrough : public Node {
+ public:
+ PassThrough() : output_(this) {}
+
+ void Process() final { output_.Write(input_.Read()[0]); }
+
+ bool CleanUp() final { return false; }
+
+ Node::Input<int*> input_;
+ Node::Output<int*> output_;
+};
+
+class IncNode : public Node {
+ public:
+ IncNode() : output_(this), inc_value_(0) {}
+
+ void Process() final {
+ inc_value_ = *input_.Read()[0];
+ ++inc_value_;
+ output_.Write(&inc_value_);
+ }
+
+ bool CleanUp() final { return false; }
+
+ Node::Input<int*> input_;
+ Node::Output<int*> output_;
+
+ private:
+ int inc_value_;
+};
+
+class SinkNode : public Node {
+ public:
+ void Process() final {}
+ bool CleanUp() final { return false; }
+
+ Node::Input<int*> input_;
+};
+
+// Class for testing multiple subscriber streams.
+class NodeTest : public ::testing::Test {
+ public:
+ void SetUp() override {}
+};
+
+TEST_F(NodeTest, SourceSink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node = std::make_shared<SinkNode>();
+ sink_node->input_.Connect(source_node, &source_node->output_);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 1U);
+ EXPECT_EQ(*data0[0], 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 1U);
+ EXPECT_EQ(*data1[0], 2);
+}
+
+TEST_F(NodeTest, SourcePassThroughSink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto copy_node = std::make_shared<PassThrough>();
+ auto sink_node = std::make_shared<SinkNode>();
+ sink_node->input_.Connect(copy_node, &copy_node->output_);
+ copy_node->input_.Connect(source_node, &source_node->output_);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 1U);
+ EXPECT_EQ(*data0[0], 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 1U);
+ EXPECT_EQ(*data1[0], 2);
+}
+
+TEST_F(NodeTest, TwoSources) {
+ static const bool kOutputNullptr = false;
+ auto source_node_a = std::make_shared<SourceNode>(kOutputNullptr);
+ auto source_node_b = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node = std::make_shared<SinkNode>();
+
+ sink_node->input_.Connect(source_node_a, &source_node_a->output_);
+ sink_node->input_.Connect(source_node_b, &source_node_b->output_);
+ EXPECT_EQ(source_node_a.use_count(), 2);
+ EXPECT_EQ(source_node_b.use_count(), 2);
+ EXPECT_EQ(sink_node.use_count(), 1);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 2U);
+ EXPECT_EQ(*data0[0], 1);
+ EXPECT_EQ(*data0[1], 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 2U);
+ EXPECT_EQ(*data1[0], 2);
+ EXPECT_EQ(*data1[1], 2);
+
+ sink_node.reset();
+ EXPECT_EQ(source_node_a.use_count(), 1);
+ EXPECT_EQ(source_node_b.use_count(), 1);
+}
+
+TEST_F(NodeTest, DoubleSink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(source_node, &source_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 3);
+
+ auto& dataA0 = sink_node_a->input_.Read();
+ auto& dataB0 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA0.size(), 1U);
+ EXPECT_EQ(dataB0.size(), 1U);
+ EXPECT_EQ(*dataA0[0], 1);
+ EXPECT_EQ(*dataB0[0], 1);
+
+ auto& dataA1 = sink_node_a->input_.Read();
+ auto& dataB1 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA1.size(), 1U);
+ EXPECT_EQ(dataB1.size(), 1U);
+ EXPECT_EQ(*dataA1[0], 2);
+ EXPECT_EQ(*dataB1[0], 2);
+
+ sink_node_a.reset();
+ EXPECT_EQ(source_node.use_count(), 2);
+ sink_node_b.reset();
+ EXPECT_EQ(source_node.use_count(), 1);
+}
+
+TEST_F(NodeTest, DoubleSinkWithIncrement) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto inc_node = std::make_shared<IncNode>();
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(inc_node, &inc_node->output_);
+ inc_node->input_.Connect(source_node, &source_node->output_);
+
+ auto& dataA0 = sink_node_a->input_.Read();
+ auto& dataB0 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA0.size(), 1U);
+ EXPECT_EQ(dataB0.size(), 1U);
+ EXPECT_EQ(*dataA0[0], 1);
+ EXPECT_EQ(*dataB0[0], 2);
+
+ auto& dataA1 = sink_node_a->input_.Read();
+ auto& dataB1 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA1.size(), 1U);
+ EXPECT_EQ(dataB1.size(), 1U);
+ EXPECT_EQ(*dataA1[0], 2);
+ EXPECT_EQ(*dataB1[0], 3);
+}
+
+TEST_F(NodeTest, DisconnectSingleLink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node = std::make_shared<SinkNode>();
+
+ sink_node->input_.Connect(source_node, &source_node->output_);
+ EXPECT_EQ(sink_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 1U);
+ EXPECT_EQ(*data0[0], 1);
+
+ sink_node->input_.Disconnect(&source_node->output_);
+ EXPECT_EQ(sink_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 0U);
+}
+
+TEST_F(NodeTest, DisconnectIntermediate) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto inc_node = std::make_shared<IncNode>();
+ auto sink_node = std::make_shared<SinkNode>();
+
+ sink_node->input_.Connect(inc_node, &inc_node->output_);
+ inc_node->input_.Connect(source_node, &source_node->output_);
+
+ inc_node->input_.Disconnect(&source_node->output_);
+ inc_node.reset();
+ EXPECT_EQ(sink_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 1);
+}
+
+TEST_F(NodeTest, DisconnectMultiLink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto inc_node = std::make_shared<IncNode>();
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+ auto sink_node_c = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(inc_node, &inc_node->output_);
+ sink_node_c->input_.Connect(inc_node, &inc_node->output_);
+ inc_node->input_.Connect(source_node, &source_node->output_);
+
+ sink_node_a->input_.Disconnect(&source_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(sink_node_c.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 3);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& dataA0 = sink_node_a->input_.Read();
+ auto& dataB0 = sink_node_b->input_.Read();
+ auto& dataC0 = sink_node_c->input_.Read();
+ EXPECT_EQ(dataA0.size(), 0U);
+ EXPECT_EQ(dataB0.size(), 1U);
+ EXPECT_EQ(*dataB0[0], 2);
+ EXPECT_EQ(dataC0.size(), 1U);
+ EXPECT_EQ(*dataC0[0], 2);
+
+ sink_node_b->input_.Disconnect(&inc_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(sink_node_c.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 2);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& dataA1 = sink_node_a->input_.Read();
+ auto& dataB1 = sink_node_b->input_.Read();
+ auto& dataC1 = sink_node_c->input_.Read();
+ EXPECT_EQ(dataA1.size(), 0U);
+ EXPECT_EQ(dataB1.size(), 0U);
+ EXPECT_EQ(dataC1.size(), 1U);
+ EXPECT_EQ(*dataC1[0], 3);
+
+ sink_node_c->input_.Disconnect(&inc_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(sink_node_c.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& dataA2 = sink_node_a->input_.Read();
+ auto& dataB2 = sink_node_b->input_.Read();
+ auto& dataC2 = sink_node_c->input_.Read();
+ EXPECT_EQ(dataA2.size(), 0U);
+ EXPECT_EQ(dataB2.size(), 0U);
+ EXPECT_EQ(dataC2.size(), 0U);
+
+ inc_node->input_.Disconnect(&source_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 1);
+}
+
+TEST_F(NodeTest, NullPtrSourceMultipleClients) {
+ static const bool kOutputNullptr = true;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(source_node, &source_node->output_);
+
+ auto& dataA = sink_node_a->input_.Read();
+ auto& dataB = sink_node_b->input_.Read();
+
+ EXPECT_TRUE(dataA.empty());
+ EXPECT_TRUE(dataB.empty());
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc
new file mode 100644
index 000000000..3053e8e6b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc
@@ -0,0 +1,89 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+ProcessingNode::NodeInput::NodeInput(
+ const std::vector<const AudioBuffer*>& input_vector)
+ : input_vector_(input_vector) {}
+
+const AudioBuffer* ProcessingNode::NodeInput::GetSingleInput() const {
+ if (input_vector_.size() == 1) {
+ return input_vector_[0];
+ }
+ if (input_vector_.size() > 1) {
+ LOG(WARNING) << "GetSingleInput() called on multi buffer input";
+ }
+ return nullptr;
+}
+
+const std::vector<const AudioBuffer*>&
+ProcessingNode::NodeInput::GetInputBuffers() const {
+ return input_vector_;
+}
+
+ProcessingNode::ProcessingNode()
+ : Node(), output_stream_(this), process_on_no_input_(false) {}
+
+void ProcessingNode::Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) {
+ input_stream_.Connect(publisher_node->GetSharedNodePtr(),
+ publisher_node->GetOutput());
+}
+
+void ProcessingNode::Process() {
+ NodeInput input(input_stream_.Read());
+ const AudioBuffer* output = nullptr;
+ // Only call AudioProcess if input data is available.
+ if (process_on_no_input_ || !input.GetInputBuffers().empty()) {
+ output = AudioProcess(input);
+ }
+ output_stream_.Write(output);
+}
+
+bool ProcessingNode::CleanUp() {
+ CallCleanUpOnInputNodes();
+ return (input_stream_.GetNumConnections() == 0);
+}
+
+void ProcessingNode::EnableProcessOnEmptyInput(bool enable) {
+ process_on_no_input_ = enable;
+}
+
+void ProcessingNode::CallCleanUpOnInputNodes() {
+ // We need to make a copy of the OutputNodeMap map since it changes due to
+ // Disconnect() calls.
+ const auto connected_nodes = input_stream_.GetConnectedNodeOutputPairs();
+ for (const auto& input_node : connected_nodes) {
+ Output<const AudioBuffer*>* output = input_node.first;
+ std::shared_ptr<Node> node = input_node.second;
+ const bool is_ready_to_be_disconnected = node->CleanUp();
+ if (is_ready_to_be_disconnected) {
+ input_stream_.Disconnect(output);
+ }
+ }
+}
+
+std::shared_ptr<Node> ProcessingNode::GetSharedNodePtr() {
+ return shared_from_this();
+}
+Node::Output<const AudioBuffer*>* ProcessingNode::GetOutput() {
+ return &output_stream_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h
new file mode 100644
index 000000000..6142c794c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h
@@ -0,0 +1,115 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_PROCESSING_NODE_H_
+#define RESONANCE_AUDIO_NODE_PROCESSING_NODE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "node/publisher_node.h"
+#include "node/subscriber_node.h"
+
+namespace vraudio {
+
+// Audio processing node that reads from multiple inputs, processes the
+// received data and outputs its result.
+class ProcessingNode : public Node,
+ public SubscriberNode<const AudioBuffer*>,
+ public PublisherNode<const AudioBuffer*> {
+ public:
+ typedef SubscriberNode<const AudioBuffer*> SubscriberNodeType;
+ typedef PublisherNode<const AudioBuffer*> PublisherNodeType;
+
+ // Helper class to manage incoming |AudioBuffer|s.
+ class NodeInput {
+ public:
+ // Constructor.
+ //
+ // @param input_vector Vector containing pointers to incoming
+ // |AudioBuffer|s.
+ explicit NodeInput(const std::vector<const AudioBuffer*>& input_vector);
+
+ // Returns a nullptr if zero or more than one input buffers are available.
+ // Otherwise a pointer to the single input |AudioBuffer| is returned. This
+ // method should be used if only a single input |AudioBuffer| is expected.
+ //
+ // @return Pointer to single input |AudioBuffer|.
+ const AudioBuffer* GetSingleInput() const;
+
+ // Returns vector with input |AudioBuffer|s.
+ //
+ // @return Pointer to single input |AudioBuffer|.
+ const std::vector<const AudioBuffer*>& GetInputBuffers() const;
+
+ // Delete copy constructor.
+ NodeInput(const NodeInput& that) = delete;
+
+ private:
+ // Const reference to vector of input |AudioBuffer|s.
+ const std::vector<const AudioBuffer*>& input_vector_;
+ };
+
+ ProcessingNode();
+
+ // SubscriberNode<InputType> implementation.
+ void Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) override;
+
+ // Node implementation.
+ void Process() final;
+ bool CleanUp() override;
+
+ // By default, calls to AudioProcess() are skipped in case of empty input
+ // buffers. This enables this node to process audio buffers in the absence of
+ // input data (which is needed for instance for a reverberation effect).
+ void EnableProcessOnEmptyInput(bool enable);
+
+ // Disable copy constructor.
+ ProcessingNode(const ProcessingNode& that) = delete;
+
+ protected:
+ // Calls |CleanUp| on all connected input nodes.
+ void CallCleanUpOnInputNodes();
+
+ // Pure virtual method to implement the audio processing method. This method
+ // receives a vector of all input arguments to be processed and requires to
+ // output a single output buffer.
+ //
+ // @param input Input instance to receive pointers to input |AudioBuffer|s.
+ // @return Returns output data.
+ virtual const AudioBuffer* AudioProcess(const NodeInput& input) = 0;
+
+ private:
+ // PublisherNode<OutputType> implementation.
+ std::shared_ptr<Node> GetSharedNodePtr() final;
+ Node::Output<const AudioBuffer*>* GetOutput() final;
+
+ // Input stream to poll for incoming data.
+ Node::Input<const AudioBuffer*> input_stream_;
+
+ // Output stream to write processed data to.
+ Node::Output<const AudioBuffer*> output_stream_;
+
+ // Flag that indicates if |AudioProcess| should be called in case no input
+ // data is available.
+ bool process_on_no_input_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_PROCESSING_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h
new file mode 100644
index 000000000..a6935da75
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h
@@ -0,0 +1,51 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_PUBLISHER_NODE_H_
+#define RESONANCE_AUDIO_NODE_PUBLISHER_NODE_H_
+
+#include <memory>
+
+#include "base/logging.h"
+#include "node/node.h"
+
+namespace vraudio {
+
+// Interface for publisher nodes that declares helper methods required to
+// connect to a publisher node. All publishing nodes need to implement this
+// interface.
+//
+// @tparam OutputType Type of the output container being streamed.
+// @interface
+template <typename OutputType>
+class PublisherNode {
+ public:
+ virtual ~PublisherNode() {}
+
+ // Creates a shared pointer of the Node instance.
+ //
+ // @return Returns a shared pointer the of Node instance.
+ virtual std::shared_ptr<Node> GetSharedNodePtr() = 0;
+
+ // Get internal Node::Output instance.
+ //
+ // @return Returns a pointer to the internal Node::Output instance.
+ virtual Node::Output<OutputType>* GetOutput() = 0;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_PUBLISHER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc
new file mode 100644
index 000000000..2d003009c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc
@@ -0,0 +1,54 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#include "node/sink_node.h"
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+SinkNode::SinkNode() : Node() {}
+
+const std::vector<const AudioBuffer*>& SinkNode::ReadInputs() {
+ return input_stream_.Read();
+}
+
+void SinkNode::Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) {
+ input_stream_.Connect(publisher_node->GetSharedNodePtr(),
+ publisher_node->GetOutput());
+}
+
+void SinkNode::Process() {
+ LOG(FATAL) << "Process should not be called on audio sink node.";
+}
+
+bool SinkNode::CleanUp() {
+ // We need to make a copy of the OutputNodeMap map since it might change due
+ // to Disconnect() calls.
+ const auto connected_nodes = input_stream_.GetConnectedNodeOutputPairs();
+ for (const auto& input_node : connected_nodes) {
+ Output<const AudioBuffer*>* output = input_node.first;
+ std::shared_ptr<Node> node = input_node.second;
+ const bool is_orphaned = node->CleanUp();
+ if (is_orphaned) {
+ input_stream_.Disconnect(output);
+ }
+ }
+ return false;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h
new file mode 100644
index 000000000..4bb3ade06
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h
@@ -0,0 +1,59 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_SINK_NODE_H_
+#define RESONANCE_AUDIO_NODE_SINK_NODE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "node/publisher_node.h"
+#include "node/subscriber_node.h"
+
+namespace vraudio {
+
+// Audio sink node that reads from multiple inputs.
+class SinkNode : public Node, public SubscriberNode<const AudioBuffer*> {
+ public:
+ typedef PublisherNode<const AudioBuffer*> PublisherNodeType;
+
+ SinkNode();
+
+ // Polls for data on all inputs of the sink node.
+ //
+ // @return A vector of input data.
+ const std::vector<const AudioBuffer*>& ReadInputs();
+
+ // SubscriberNode<AudioBuffer> implementation.
+ void Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) override;
+
+ // Node implementation.
+ void Process() final;
+ bool CleanUp() final;
+
+ // Disable copy constructor.
+ SinkNode(const SinkNode& that) = delete;
+
+ private:
+ // Input stream to poll for incoming data.
+ Node::Input<const AudioBuffer*> input_stream_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_SINK_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc
new file mode 100644
index 000000000..8567df6c6
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc
@@ -0,0 +1,41 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#include "node/source_node.h"
+
+namespace vraudio {
+
+SourceNode::SourceNode()
+ : Node(), output_stream_(this), end_of_stream_(false) {}
+
+void SourceNode::Process() {
+ const AudioBuffer* output = AudioProcess();
+ output_stream_.Write(output);
+}
+
+bool SourceNode::CleanUp() { return end_of_stream_; }
+
+std::shared_ptr<Node> SourceNode::GetSharedNodePtr() {
+ return shared_from_this();
+}
+
+Node::Output<const AudioBuffer*>* SourceNode::GetOutput() {
+ return &output_stream_;
+}
+
+void SourceNode::MarkEndOfStream() { end_of_stream_ = true; }
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/source_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.h
new file mode 100644
index 000000000..73a598637
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.h
@@ -0,0 +1,68 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_SOURCE_NODE_H_
+#define RESONANCE_AUDIO_NODE_SOURCE_NODE_H_
+
+#include <atomic>
+#include <memory>
+
+#include "base/audio_buffer.h"
+#include "node/publisher_node.h"
+
+namespace vraudio {
+
+// Audio source node that outputs data via the AudioProcess() method.
+class SourceNode : public Node, public PublisherNode<const AudioBuffer*> {
+ public:
+ typedef PublisherNode<const AudioBuffer*> PublisherNodeType;
+
+ SourceNode();
+
+ // Node implementation.
+ void Process() final;
+ bool CleanUp() final;
+
+ // PublisherNode<OutputType> implementation.
+ std::shared_ptr<Node> GetSharedNodePtr() final;
+ Node::Output<const AudioBuffer*>* GetOutput() final;
+
+ // Marks this node as being out of data and to be removed during the next
+ // clean-up cycle.
+ void MarkEndOfStream();
+
+ // Disable copy constructor.
+ SourceNode(const SourceNode& that) = delete;
+
+ protected:
+ // Pure virtual method to implement the audio processing method. This method
+ // requires to return a single output buffer that can be processed by the node
+ // subscribers.
+ //
+ // @return Returns output data.
+ virtual const AudioBuffer* AudioProcess() = 0;
+
+ private:
+ // Output stream to write processed data to.
+ Node::Output<const AudioBuffer*> output_stream_;
+
+ // Flag indicating if this source node can be removed.
+ std::atomic<bool> end_of_stream_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_SOURCE_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h
new file mode 100644
index 000000000..f297ce9e4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h
@@ -0,0 +1,48 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+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.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_SUBSCRIBER_NODE_H_
+#define RESONANCE_AUDIO_NODE_SUBSCRIBER_NODE_H_
+
+#include <memory>
+
+#include "base/logging.h"
+#include "node/publisher_node.h"
+
+namespace vraudio {
+
+// Interface for subscriber nodes that declares the connection and
+// disconnection methods. All subscribing nodes need to implement this
+// interface.
+//
+// @tparam InputType Input data type, i. e., the output data type of nodes to
+// connect to.
+// @interface
+template <typename InputType>
+class SubscriberNode {
+ public:
+ virtual ~SubscriberNode() {}
+
+ // Connects this node to |publisher_node|.
+ //
+ // @param publisher_node Publisher node to connect to.
+ virtual void Connect(
+ const std::shared_ptr<PublisherNode<InputType>>& publisher_node) = 0;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_SUBSCRIBER_NODE_H_