Home | History | Annotate | Download | only in flowgraph
      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:
     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 
     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 
    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      */
    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:
    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:
    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      */
    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      */
    216     void connect(AudioFloatOutputPort *port) {
    217         assert(getSamplesPerFrame() == port->getSamplesPerFrame());
    218         mConnected = port;
    219     }
    220 
    221     void disconnect(AudioFloatOutputPort *port) {
    222         assert(mConnected == port);
    223         (void) port;
    224         mConnected = nullptr;
    225     }
    226 
    227     void disconnect() {
    228         mConnected = nullptr;
    229     }
    230 
    231 private:
    232     AudioFloatOutputPort *mConnected = nullptr;
    233 };
    234 
    235 /***************************************************************************/
    236 class AudioSource : public AudioProcessorBase {
    237 public:
    238     explicit AudioSource(int32_t channelCount)
    239             : output(*this, channelCount) {
    240     }
    241 
    242     virtual ~AudioSource() = default;
    243 
    244     AudioFloatOutputPort output;
    245 
    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:
    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      */
    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