summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/resonance-audio/resonance_audio/node/node.h
blob: e6e10e39d1d37deadf057370e29f50aa7f1455da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
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_