diff options
Diffstat (limited to 'src/3rdparty/resonance-audio/resonance_audio/node')
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, ©_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_ |