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