blob: a2380c0ffd5380c8639df3e2f740d0a86c942c1c [file] [log] [blame]
Phil Burk0817a2b2019-01-03 14:50:18 -08001/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * AudioProcessorBase.h
19 *
20 * Audio processing node and ports that can be used in a simple data flow graph.
21 */
22
23#ifndef FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
24#define FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
25
26#include <cassert>
27#include <cstring>
28#include <math.h>
Phil Burk91362012019-03-04 12:00:30 -080029#include <memory>
Phil Burk0817a2b2019-01-03 14:50:18 -080030#include <sys/types.h>
31#include <time.h>
32#include <unistd.h>
Phil Burk22f21132019-03-08 12:43:19 -080033#include <vector>
Phil Burk0817a2b2019-01-03 14:50:18 -080034
Phil Burk91362012019-03-04 12:00:30 -080035// TODO Move these classes into separate files.
36// TODO Maybe remove "Audio" prefix from these class names: AudioProcessorBase to ProcessorNode
37// TODO Review use of raw pointers for connect(). Maybe use smart pointers but need to avoid
38// run-time deallocation in audio thread.
39
Phil Burk51ca7552019-01-04 10:59:13 -080040// Set this to 1 if using it inside the Android framework.
Phil Burk91362012019-03-04 12:00:30 -080041// This code is kept here so that it can be moved easily between Oboe and AAudio.
Phil Burk0817a2b2019-01-03 14:50:18 -080042#define FLOWGRAPH_ANDROID_INTERNAL 0
43
44namespace flowgraph {
45
Phil Burk91362012019-03-04 12:00:30 -080046// Default block size that can be overridden when the AudioFloatBufferPort is created.
Phil Burk0817a2b2019-01-03 14:50:18 -080047// If it is too small then we will have too much overhead from switching between nodes.
48// If it is too high then we will thrash the caches.
Phil Burk91362012019-03-04 12:00:30 -080049constexpr int kDefaultBufferSize = 8; // arbitrary
Phil Burk0817a2b2019-01-03 14:50:18 -080050
Phil Burk22f21132019-03-08 12:43:19 -080051class AudioPort;
Phil Burk0817a2b2019-01-03 14:50:18 -080052class AudioFloatInputPort;
53
54/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -080055/**
56 * Base class for all nodes in the flowgraph.
57 */
Phil Burk0817a2b2019-01-03 14:50:18 -080058class AudioProcessorBase {
59public:
Phil Burkbd76d6a2019-06-01 08:48:03 -070060 AudioProcessorBase() {}
Phil Burk0817a2b2019-01-03 14:50:18 -080061 virtual ~AudioProcessorBase() = default;
62
63 /**
Phil Burk91362012019-03-04 12:00:30 -080064 * Read from the input ports,
65 * generate multiple frames of data then write the results to the output ports.
Phil Burk0817a2b2019-01-03 14:50:18 -080066 *
Phil Burk0817a2b2019-01-03 14:50:18 -080067 * @param numFrames maximum number of frames requested for processing
68 * @return number of frames actually processed
69 */
Phil Burk22f21132019-03-08 12:43:19 -080070 virtual int32_t onProcess(int32_t numFrames) = 0;
Phil Burk0817a2b2019-01-03 14:50:18 -080071
72 /**
73 * If the framePosition is at or after the last frame position then call onProcess().
74 * This prevents infinite recursion in case of cyclic graphs.
75 * It also prevents nodes upstream from a branch from being executed twice.
76 *
77 * @param framePosition
78 * @param numFrames
79 * @return number of frames valid
80 */
81 int32_t pullData(int64_t framePosition, int32_t numFrames);
82
Phil Burkf67a97f2019-05-30 12:48:17 -070083 /**
84 * Recursively reset all the nodes in the graph, starting from a Sink.
Phil Burk41b868c2019-08-28 15:39:05 -070085 *
86 * This must not be called at the same time as pullData!
Phil Burkf67a97f2019-05-30 12:48:17 -070087 */
88 void pullReset();
89
90 /**
91 * Reset framePosition counters.
92 */
93 virtual void reset();
94
Phil Burk22f21132019-03-08 12:43:19 -080095 void addInputPort(AudioPort &port) {
96 mInputPorts.push_back(port);
97 }
98
Phil Burkdd3971d2019-05-17 18:42:58 -070099 bool isDataPulledAutomatically() const {
100 return mDataPulledAutomatically;
101 }
102
103 /**
104 * Set true if you want the data pulled through the graph automatically.
105 * This is the default.
106 *
Phil Burk41b868c2019-08-28 15:39:05 -0700107 * Set false if you want to pull the data from the input ports in the onProcess() method.
Phil Burkdd3971d2019-05-17 18:42:58 -0700108 * You might do this, for example, in a sample rate converting node.
109 *
110 * @param automatic
111 */
112 void setDataPulledAutomatically(bool automatic) {
113 mDataPulledAutomatically = automatic;
114 }
115
Phil Burkf67a97f2019-05-30 12:48:17 -0700116 virtual const char *getName() {
117 return "AudioProcessorBase";
118 }
119
120 int64_t getLastFramePosition() {
121 return mLastFramePosition;
122 }
123
Phil Burk0817a2b2019-01-03 14:50:18 -0800124protected:
Phil Burkf67a97f2019-05-30 12:48:17 -0700125 int64_t mLastFramePosition = 0;
Phil Burk0817a2b2019-01-03 14:50:18 -0800126
Phil Burk22f21132019-03-08 12:43:19 -0800127 std::vector<std::reference_wrapper<AudioPort>> mInputPorts;
128
Phil Burk0817a2b2019-01-03 14:50:18 -0800129private:
Phil Burkdd3971d2019-05-17 18:42:58 -0700130 bool mDataPulledAutomatically = true;
Phil Burkf67a97f2019-05-30 12:48:17 -0700131 bool mBlockRecursion = false;
132 int32_t mLastFrameCount = 0;
Phil Burkdd3971d2019-05-17 18:42:58 -0700133
Phil Burk0817a2b2019-01-03 14:50:18 -0800134};
135
136/***************************************************************************/
137/**
138 * This is a connector that allows data to flow between modules.
Phil Burk72356eb2019-01-09 10:35:51 -0800139 *
140 * The ports are the primary means of interacting with a module.
141 * So they are generally declared as public.
142 *
Phil Burk0817a2b2019-01-03 14:50:18 -0800143 */
144class AudioPort {
145public:
146 AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
Phil Burk2da4a842019-08-16 12:15:23 -0700147 : mContainingNode(parent)
Phil Burk0817a2b2019-01-03 14:50:18 -0800148 , mSamplesPerFrame(samplesPerFrame) {
149 }
150
151 // Ports are often declared public. So let's make them non-copyable.
152 AudioPort(const AudioPort&) = delete;
153 AudioPort& operator=(const AudioPort&) = delete;
154
155 int32_t getSamplesPerFrame() const {
156 return mSamplesPerFrame;
157 }
158
Phil Burk22f21132019-03-08 12:43:19 -0800159 virtual int32_t pullData(int64_t framePosition, int32_t numFrames) = 0;
160
Phil Burkf67a97f2019-05-30 12:48:17 -0700161 virtual void pullReset() {}
162
Phil Burk0817a2b2019-01-03 14:50:18 -0800163protected:
Phil Burk2da4a842019-08-16 12:15:23 -0700164 AudioProcessorBase &mContainingNode;
Phil Burk0817a2b2019-01-03 14:50:18 -0800165
166private:
167 const int32_t mSamplesPerFrame = 1;
168};
169
170/***************************************************************************/
171/**
Phil Burk91362012019-03-04 12:00:30 -0800172 * This port contains a 32-bit float buffer that can contain several frames of data.
173 * Processing the data in a block improves performance.
174 *
175 * The size is framesPerBuffer * samplesPerFrame).
Phil Burk0817a2b2019-01-03 14:50:18 -0800176 */
Phil Burk91362012019-03-04 12:00:30 -0800177class AudioFloatBufferPort : public AudioPort {
Phil Burk0817a2b2019-01-03 14:50:18 -0800178public:
Phil Burk776b8b52019-07-10 16:02:12 -0700179 AudioFloatBufferPort(AudioProcessorBase &parent,
Phil Burk0817a2b2019-01-03 14:50:18 -0800180 int32_t samplesPerFrame,
Phil Burk91362012019-03-04 12:00:30 -0800181 int32_t framesPerBuffer = kDefaultBufferSize
Phil Burk0817a2b2019-01-03 14:50:18 -0800182 );
183
Phil Burk91362012019-03-04 12:00:30 -0800184 virtual ~AudioFloatBufferPort() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800185
Phil Burk91362012019-03-04 12:00:30 -0800186 int32_t getFramesPerBuffer() const {
187 return mFramesPerBuffer;
Phil Burk0817a2b2019-01-03 14:50:18 -0800188 }
189
190protected:
191
192 /**
193 * @return buffer internal to the port or from a connected port
194 */
Phil Burk91362012019-03-04 12:00:30 -0800195 virtual float *getBuffer() {
196 return mBuffer.get();
Phil Burk0817a2b2019-01-03 14:50:18 -0800197 }
198
Phil Burk0817a2b2019-01-03 14:50:18 -0800199private:
Phil Burk91362012019-03-04 12:00:30 -0800200 const int32_t mFramesPerBuffer = 1;
201 std::unique_ptr<float[]> mBuffer; // allocated in constructor
Phil Burk0817a2b2019-01-03 14:50:18 -0800202};
203
204/***************************************************************************/
205/**
Phil Burk91362012019-03-04 12:00:30 -0800206 * The results of a node's processing are stored in the buffers of the output ports.
Phil Burk0817a2b2019-01-03 14:50:18 -0800207 */
Phil Burk91362012019-03-04 12:00:30 -0800208class AudioFloatOutputPort : public AudioFloatBufferPort {
Phil Burk0817a2b2019-01-03 14:50:18 -0800209public:
210 AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
Phil Burk91362012019-03-04 12:00:30 -0800211 : AudioFloatBufferPort(parent, samplesPerFrame) {
Phil Burk0817a2b2019-01-03 14:50:18 -0800212 }
213
214 virtual ~AudioFloatOutputPort() = default;
215
Phil Burk91362012019-03-04 12:00:30 -0800216 using AudioFloatBufferPort::getBuffer;
Phil Burk0817a2b2019-01-03 14:50:18 -0800217
218 /**
Phil Burk0817a2b2019-01-03 14:50:18 -0800219 * Connect to the input of another module.
220 * An input port can only have one connection.
221 * An output port can have multiple connections.
222 * If you connect a second output port to an input port
223 * then it overwrites the previous connection.
224 *
Phil Burk51ca7552019-01-04 10:59:13 -0800225 * This not thread safe. Do not modify the graph topology from another thread while running.
226 * Also do not delete a module while it is connected to another port if the graph is running.
Phil Burk0817a2b2019-01-03 14:50:18 -0800227 */
228 void connect(AudioFloatInputPort *port);
229
230 /**
231 * Disconnect from the input of another module.
232 * This not thread safe.
233 */
234 void disconnect(AudioFloatInputPort *port);
Phil Burk22f21132019-03-08 12:43:19 -0800235
236 /**
237 * Call the parent module's onProcess() method.
238 * That may pull data from its inputs and recursively
239 * process the entire graph.
240 * @return number of frames actually pulled
241 */
242 int32_t pullData(int64_t framePosition, int32_t numFrames) override;
243
Phil Burkf67a97f2019-05-30 12:48:17 -0700244
245 void pullReset() override;
246
Phil Burk0817a2b2019-01-03 14:50:18 -0800247};
248
249/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -0800250
251/**
252 * An input port for streaming audio data.
253 * You can set a value that will be used for processing.
254 * If you connect an output port to this port then its value will be used instead.
255 */
256class AudioFloatInputPort : public AudioFloatBufferPort {
Phil Burk0817a2b2019-01-03 14:50:18 -0800257public:
258 AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
Phil Burk91362012019-03-04 12:00:30 -0800259 : AudioFloatBufferPort(parent, samplesPerFrame) {
Phil Burk22f21132019-03-08 12:43:19 -0800260 // Add to parent so it can pull data from each input.
261 parent.addInputPort(*this);
Phil Burk0817a2b2019-01-03 14:50:18 -0800262 }
263
264 virtual ~AudioFloatInputPort() = default;
265
266 /**
267 * If connected to an output port then this will return
268 * that output ports buffers.
269 * If not connected then it returns the input ports own buffer
270 * which can be loaded using setValue().
271 */
Phil Burk91362012019-03-04 12:00:30 -0800272 float *getBuffer() override;
Phil Burk0817a2b2019-01-03 14:50:18 -0800273
274 /**
Phil Burk0817a2b2019-01-03 14:50:18 -0800275 * Write every value of the float buffer.
276 * This value will be ignored if an output port is connected
277 * to this port.
278 */
279 void setValue(float value) {
Phil Burk91362012019-03-04 12:00:30 -0800280 int numFloats = kDefaultBufferSize * getSamplesPerFrame();
281 float *buffer = getBuffer();
Phil Burk0817a2b2019-01-03 14:50:18 -0800282 for (int i = 0; i < numFloats; i++) {
283 *buffer++ = value;
284 }
285 }
286
287 /**
288 * Connect to the output of another module.
289 * An input port can only have one connection.
290 * An output port can have multiple connections.
291 * This not thread safe.
292 */
293 void connect(AudioFloatOutputPort *port) {
294 assert(getSamplesPerFrame() == port->getSamplesPerFrame());
295 mConnected = port;
296 }
297
298 void disconnect(AudioFloatOutputPort *port) {
299 assert(mConnected == port);
300 (void) port;
301 mConnected = nullptr;
302 }
303
304 void disconnect() {
305 mConnected = nullptr;
306 }
307
Phil Burk22f21132019-03-08 12:43:19 -0800308 /**
309 * Pull data from any output port that is connected.
310 */
311 int32_t pullData(int64_t framePosition, int32_t numFrames) override;
312
Phil Burkf67a97f2019-05-30 12:48:17 -0700313 void pullReset() override;
314
Phil Burk0817a2b2019-01-03 14:50:18 -0800315private:
316 AudioFloatOutputPort *mConnected = nullptr;
317};
318
319/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -0800320
321/**
322 * Base class for an edge node in a graph that has no upstream nodes.
323 * It outputs data but does not consume data.
324 * By default, it will read its data from an external buffer.
325 */
Phil Burk0817a2b2019-01-03 14:50:18 -0800326class AudioSource : public AudioProcessorBase {
327public:
328 explicit AudioSource(int32_t channelCount)
329 : output(*this, channelCount) {
330 }
331
332 virtual ~AudioSource() = default;
333
334 AudioFloatOutputPort output;
Phil Burk561cda32019-07-11 12:40:52 -0700335};
336
337/***************************************************************************/
338
339/**
340 * Base class for an edge node in a graph that has no upstream nodes.
341 * It outputs data but does not consume data.
342 * By default, it will read its data from an external buffer.
343 */
344class AudioSourceBuffered : public AudioSource {
345public:
346 explicit AudioSourceBuffered(int32_t channelCount)
347 : AudioSource(channelCount) {}
348
349 virtual ~AudioSourceBuffered() = default;
Phil Burk0817a2b2019-01-03 14:50:18 -0800350
Phil Burk91362012019-03-04 12:00:30 -0800351 /**
352 * Specify buffer that the node will read from.
353 *
354 * @param data TODO Consider using std::shared_ptr.
355 * @param numFrames
356 */
Phil Burk0817a2b2019-01-03 14:50:18 -0800357 void setData(const void *data, int32_t numFrames) {
358 mData = data;
359 mSizeInFrames = numFrames;
360 mFrameIndex = 0;
361 }
362
363protected:
364 const void *mData = nullptr;
365 int32_t mSizeInFrames = 0; // number of frames in mData
366 int32_t mFrameIndex = 0; // index of next frame to be processed
367};
368
369/***************************************************************************/
Phil Burk91362012019-03-04 12:00:30 -0800370/**
371 * Base class for an edge node in a graph that has no downstream nodes.
372 * It consumes data but does not output data.
373 * This graph will be executed when data is read() from this node
374 * by pulling data from upstream nodes.
375 */
Phil Burk0817a2b2019-01-03 14:50:18 -0800376class AudioSink : public AudioProcessorBase {
377public:
378 explicit AudioSink(int32_t channelCount)
379 : input(*this, channelCount) {
380 }
381
382 virtual ~AudioSink() = default;
383
384 AudioFloatInputPort input;
385
386 /**
387 * Dummy processor. The work happens in the read() method.
388 *
Phil Burk0817a2b2019-01-03 14:50:18 -0800389 * @param numFrames
390 * @return number of frames actually processed
391 */
Phil Burk22f21132019-03-08 12:43:19 -0800392 int32_t onProcess(int32_t numFrames) override {
393 return numFrames;
Phil Burk84e7e652019-05-13 16:29:15 -0700394 }
Phil Burk0817a2b2019-01-03 14:50:18 -0800395
Phil Burk22f21132019-03-08 12:43:19 -0800396 virtual int32_t read(int64_t framePosition, void *data, int32_t numFrames) = 0;
Phil Burk0817a2b2019-01-03 14:50:18 -0800397
Phil Burk0817a2b2019-01-03 14:50:18 -0800398};
399
Phil Burkf5b3b812019-06-04 07:51:07 -0700400/***************************************************************************/
401/**
Phil Burk2da4a842019-08-16 12:15:23 -0700402 * Base class for a node that has an input and an output with the same number of channels.
Phil Burkf5b3b812019-06-04 07:51:07 -0700403 * This may include traditional filters, eg. FIR, but also include
404 * any processing node that converts input to output.
405 */
406class AudioFilter : public AudioProcessorBase {
407public:
408 explicit AudioFilter(int32_t channelCount)
409 : input(*this, channelCount)
410 , output(*this, channelCount) {
411 }
412
413 virtual ~AudioFilter() = default;
414
415 AudioFloatInputPort input;
416 AudioFloatOutputPort output;
417};
418
Phil Burk0817a2b2019-01-03 14:50:18 -0800419} /* namespace flowgraph */
420
421#endif /* FLOWGRAPH_AUDIO_PROCESSOR_BASE_H */