• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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>
29 #include <sys/types.h>
30 #include <time.h>
31 #include <unistd.h>
32 
33 // TODO consider publishing all header files under "include/libaaudio/FlowGraph.h"
34 
35 namespace flowgraph {
36 
37 // Default block size that can be overridden when the AudioFloatBlockPort is created.
38 // If it is too small then we will have too much overhead from switching between nodes.
39 // If it is too high then we will thrash the caches.
40 constexpr int kDefaultBlockSize = 8; // arbitrary
41 
42 class AudioFloatInputPort;
43 
44 /***************************************************************************/
45 class AudioProcessorBase {
46 public:
47     virtual ~AudioProcessorBase() = default;
48 
49     /**
50      * Perform custom function.
51      *
52      * @param framePosition index of first frame to be processed
53      * @param numFrames maximum number of frames requested for processing
54      * @return number of frames actually processed
55      */
56     virtual int32_t onProcess(int64_t framePosition, int32_t numFrames) = 0;
57 
58     /**
59      * If the framePosition is at or after the last frame position then call onProcess().
60      * This prevents infinite recursion in case of cyclic graphs.
61      * It also prevents nodes upstream from a branch from being executed twice.
62      *
63      * @param framePosition
64      * @param numFrames
65      * @return
66      */
67     int32_t pullData(int64_t framePosition, int32_t numFrames);
68 
69 protected:
70     int64_t  mLastFramePosition = -1; // Start at -1 so that the first pull works.
71 
72 private:
73     int32_t  mFramesValid = 0; // num valid frames in the block
74 };
75 
76 /***************************************************************************/
77 /**
78   * This is a connector that allows data to flow between modules.
79   */
80 class AudioPort {
81 public:
AudioPort(AudioProcessorBase & parent,int32_t samplesPerFrame)82     AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
83             : mParent(parent)
84             , mSamplesPerFrame(samplesPerFrame) {
85     }
86 
87     // Ports are often declared public. So let's make them non-copyable.
88     AudioPort(const AudioPort&) = delete;
89     AudioPort& operator=(const AudioPort&) = delete;
90 
getSamplesPerFrame()91     int32_t getSamplesPerFrame() const {
92         return mSamplesPerFrame;
93     }
94 
95 protected:
96     AudioProcessorBase &mParent;
97 
98 private:
99     const int32_t    mSamplesPerFrame = 1;
100 };
101 
102 /***************************************************************************/
103 /**
104  * This port contains a float type buffer.
105  * The size is framesPerBlock * samplesPerFrame).
106  */
107 class AudioFloatBlockPort  : public AudioPort {
108 public:
109     AudioFloatBlockPort(AudioProcessorBase &mParent,
110                    int32_t samplesPerFrame,
111                    int32_t framesPerBlock = kDefaultBlockSize
112                 );
113 
114     virtual ~AudioFloatBlockPort();
115 
getFramesPerBlock()116     int32_t getFramesPerBlock() const {
117         return mFramesPerBlock;
118     }
119 
120 protected:
121 
122     /**
123      * @return buffer internal to the port or from a connected port
124      */
getBlock()125     virtual float *getBlock() {
126         return mSampleBlock;
127     }
128 
129 
130 private:
131     const int32_t    mFramesPerBlock = 1;
132     float           *mSampleBlock = nullptr; // allocated in constructor
133 };
134 
135 /***************************************************************************/
136 /**
137   * The results of a module are stored in the buffer of the output ports.
138   */
139 class AudioFloatOutputPort : public AudioFloatBlockPort {
140 public:
AudioFloatOutputPort(AudioProcessorBase & parent,int32_t samplesPerFrame)141     AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
142             : AudioFloatBlockPort(parent, samplesPerFrame) {
143     }
144 
145     virtual ~AudioFloatOutputPort() = default;
146 
147     using AudioFloatBlockPort::getBlock;
148 
149     /**
150      * Call the parent module's onProcess() method.
151      * That may pull data from its inputs and recursively
152      * process the entire graph.
153      * @return number of frames actually pulled
154      */
155     int32_t pullData(int64_t framePosition, int32_t numFrames);
156 
157     /**
158      * Connect to the input of another module.
159      * An input port can only have one connection.
160      * An output port can have multiple connections.
161      * If you connect a second output port to an input port
162      * then it overwrites the previous connection.
163      *
164      * This not thread safe. Do not modify the graph topology form another thread while running.
165      */
166     void connect(AudioFloatInputPort *port);
167 
168     /**
169      * Disconnect from the input of another module.
170      * This not thread safe.
171      */
172     void disconnect(AudioFloatInputPort *port);
173 };
174 
175 /***************************************************************************/
176 class AudioFloatInputPort : public AudioFloatBlockPort {
177 public:
AudioFloatInputPort(AudioProcessorBase & parent,int32_t samplesPerFrame)178     AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
179             : AudioFloatBlockPort(parent, samplesPerFrame) {
180     }
181 
182     virtual ~AudioFloatInputPort() = default;
183 
184     /**
185      * If connected to an output port then this will return
186      * that output ports buffers.
187      * If not connected then it returns the input ports own buffer
188      * which can be loaded using setValue().
189      */
190     float *getBlock() override;
191 
192     /**
193      * Pull data from any output port that is connected.
194      */
195     int32_t pullData(int64_t framePosition, int32_t numFrames);
196 
197     /**
198      * Write every value of the float buffer.
199      * This value will be ignored if an output port is connected
200      * to this port.
201      */
setValue(float value)202     void setValue(float value) {
203         int numFloats = kDefaultBlockSize * getSamplesPerFrame();
204         float *buffer = getBlock();
205         for (int i = 0; i < numFloats; i++) {
206             *buffer++ = value;
207         }
208     }
209 
210     /**
211      * Connect to the output of another module.
212      * An input port can only have one connection.
213      * An output port can have multiple connections.
214      * This not thread safe.
215      */
connect(AudioFloatOutputPort * port)216     void connect(AudioFloatOutputPort *port) {
217         assert(getSamplesPerFrame() == port->getSamplesPerFrame());
218         mConnected = port;
219     }
220 
disconnect(AudioFloatOutputPort * port)221     void disconnect(AudioFloatOutputPort *port) {
222         assert(mConnected == port);
223         (void) port;
224         mConnected = nullptr;
225     }
226 
disconnect()227     void disconnect() {
228         mConnected = nullptr;
229     }
230 
231 private:
232     AudioFloatOutputPort *mConnected = nullptr;
233 };
234 
235 /***************************************************************************/
236 class AudioSource : public AudioProcessorBase {
237 public:
AudioSource(int32_t channelCount)238     explicit AudioSource(int32_t channelCount)
239             : output(*this, channelCount) {
240     }
241 
242     virtual ~AudioSource() = default;
243 
244     AudioFloatOutputPort output;
245 
setData(const void * data,int32_t numFrames)246     void setData(const void *data, int32_t numFrames) {
247         mData = data;
248         mSizeInFrames = numFrames;
249         mFrameIndex = 0;
250     }
251 
252 protected:
253     const void *mData = nullptr;
254     int32_t     mSizeInFrames = 0; // number of frames in mData
255     int32_t     mFrameIndex = 0; // index of next frame to be processed
256 };
257 
258 /***************************************************************************/
259 class AudioSink : public AudioProcessorBase {
260 public:
AudioSink(int32_t channelCount)261     explicit AudioSink(int32_t channelCount)
262             : input(*this, channelCount) {
263     }
264 
265     virtual ~AudioSink() = default;
266 
267     AudioFloatInputPort input;
268 
269     /**
270      * Dummy processor. The work happens in the read() method.
271      *
272      * @param framePosition index of first frame to be processed
273      * @param numFrames
274      * @return number of frames actually processed
275      */
onProcess(int64_t framePosition,int32_t numFrames)276     int32_t onProcess(int64_t framePosition, int32_t numFrames) override {
277         (void) framePosition;
278         (void) numFrames;
279         return 0;
280     };
281 
282     virtual int32_t read(void *data, int32_t numFrames) = 0;
283 
284 protected:
285     int32_t pull(int32_t numFrames);
286 
287 private:
288     int64_t mFramePosition = 0;
289 };
290 
291 } /* namespace flowgraph */
292 
293 #endif /* FLOWGRAPH_AUDIO_PROCESSOR_BASE_H */
294