aaudio: add simple flowgraph system

Implement a simple data flow model that can be used to
chain various processing modules including:
    data format conversion
    volume ramping
    channel conversion

Bug: 65067568
Test: test_flowgraph.cpp
Change-Id: I81a5655406cfa8c1c7d7cef4d933879f823939a5
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index b9e28a0..4b5f30d 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -6,6 +6,7 @@
         "client",
         "core",
         "fifo",
+        "flowgraph",
         "legacy",
         "utility",
     ],
@@ -42,6 +43,16 @@
         "binding/RingBufferParcelable.cpp",
         "binding/SharedMemoryParcelable.cpp",
         "binding/SharedRegionParcelable.cpp",
+        "flowgraph/AudioProcessorBase.cpp",
+        "flowgraph/ClipToRange.cpp",
+        "flowgraph/MonoToMultiConverter.cpp",
+        "flowgraph/RampLinear.cpp",
+        "flowgraph/SinkFloat.cpp",
+        "flowgraph/SinkI16.cpp",
+        "flowgraph/SinkI24.cpp",
+        "flowgraph/SourceFloat.cpp",
+        "flowgraph/SourceI16.cpp",
+        "flowgraph/SourceI24.cpp",
     ],
 
     cflags: [
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp
new file mode 100644
index 0000000..5667fdb
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <sys/types.h>
+#include "AudioProcessorBase.h"
+
+using namespace flowgraph;
+
+/***************************************************************************/
+int32_t AudioProcessorBase::pullData(int64_t framePosition, int32_t numFrames) {
+    if (framePosition > mLastFramePosition) {
+        mLastFramePosition = framePosition;
+        mFramesValid = onProcess(framePosition, numFrames);
+    }
+    return mFramesValid;
+}
+
+/***************************************************************************/
+AudioFloatBlockPort::AudioFloatBlockPort(AudioProcessorBase &parent,
+                               int32_t samplesPerFrame,
+                               int32_t framesPerBlock)
+        : AudioPort(parent, samplesPerFrame)
+        , mFramesPerBlock(framesPerBlock)
+        , mSampleBlock(NULL) {
+    int32_t numFloats = framesPerBlock * getSamplesPerFrame();
+    mSampleBlock = new float[numFloats]{0.0f};
+}
+
+AudioFloatBlockPort::~AudioFloatBlockPort() {
+    delete[] mSampleBlock;
+}
+
+/***************************************************************************/
+int32_t AudioFloatOutputPort::pullData(int64_t framePosition, int32_t numFrames) {
+    numFrames = std::min(getFramesPerBlock(), numFrames);
+    return mParent.pullData(framePosition, numFrames);
+}
+
+// These need to be in the .cpp file because of forward cross references.
+void AudioFloatOutputPort::connect(AudioFloatInputPort *port) {
+    port->connect(this);
+}
+
+void AudioFloatOutputPort::disconnect(AudioFloatInputPort *port) {
+    port->disconnect(this);
+}
+
+/***************************************************************************/
+int32_t AudioFloatInputPort::pullData(int64_t framePosition, int32_t numFrames) {
+    return (mConnected == NULL)
+            ? std::min(getFramesPerBlock(), numFrames)
+            : mConnected->pullData(framePosition, numFrames);
+}
+
+float *AudioFloatInputPort::getBlock() {
+    if (mConnected == NULL) {
+        return AudioFloatBlockPort::getBlock(); // loaded using setValue()
+    } else {
+        return mConnected->getBlock();
+    }
+}
+
+/***************************************************************************/
+int32_t AudioSink::pull(int32_t numFrames) {
+    int32_t actualFrames = input.pullData(mFramePosition, numFrames);
+    mFramePosition += actualFrames;
+    return actualFrames;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.h b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
new file mode 100644
index 0000000..eda46ae
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+/*
+ * AudioProcessorBase.h
+ *
+ * Audio processing node and ports that can be used in a simple data flow graph.
+ */
+
+#ifndef FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
+#define FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
+
+#include <cassert>
+#include <cstring>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+// TODO consider publishing all header files under "include/libaaudio/FlowGraph.h"
+
+namespace flowgraph {
+
+// Default block size that can be overridden when the AudioFloatBlockPort is created.
+// If it is too small then we will have too much overhead from switching between nodes.
+// If it is too high then we will thrash the caches.
+constexpr int kDefaultBlockSize = 8; // arbitrary
+
+class AudioFloatInputPort;
+
+/***************************************************************************/
+class AudioProcessorBase {
+public:
+    virtual ~AudioProcessorBase() = default;
+
+    /**
+     * Perform custom function.
+     *
+     * @param framePosition index of first frame to be processed
+     * @param numFrames maximum number of frames requested for processing
+     * @return number of frames actually processed
+     */
+    virtual int32_t onProcess(int64_t framePosition, int32_t numFrames) = 0;
+
+    /**
+     * If the framePosition is at or after the last frame position then call onProcess().
+     * This prevents infinite recursion in case of cyclic graphs.
+     * It also prevents nodes upstream from a branch from being executed twice.
+     *
+     * @param framePosition
+     * @param numFrames
+     * @return
+     */
+    int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+protected:
+    int64_t  mLastFramePosition = -1; // Start at -1 so that the first pull works.
+
+private:
+    int32_t  mFramesValid = 0; // num valid frames in the block
+};
+
+/***************************************************************************/
+/**
+  * This is a connector that allows data to flow between modules.
+  */
+class AudioPort {
+public:
+    AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+            : mParent(parent)
+            , mSamplesPerFrame(samplesPerFrame) {
+    }
+
+    // Ports are often declared public. So let's make them non-copyable.
+    AudioPort(const AudioPort&) = delete;
+    AudioPort& operator=(const AudioPort&) = delete;
+
+    int32_t getSamplesPerFrame() const {
+        return mSamplesPerFrame;
+    }
+
+protected:
+    AudioProcessorBase &mParent;
+
+private:
+    const int32_t    mSamplesPerFrame = 1;
+};
+
+/***************************************************************************/
+/**
+ * This port contains a float type buffer.
+ * The size is framesPerBlock * samplesPerFrame).
+ */
+class AudioFloatBlockPort  : public AudioPort {
+public:
+    AudioFloatBlockPort(AudioProcessorBase &mParent,
+                   int32_t samplesPerFrame,
+                   int32_t framesPerBlock = kDefaultBlockSize
+                );
+
+    virtual ~AudioFloatBlockPort();
+
+    int32_t getFramesPerBlock() const {
+        return mFramesPerBlock;
+    }
+
+protected:
+
+    /**
+     * @return buffer internal to the port or from a connected port
+     */
+    virtual float *getBlock() {
+        return mSampleBlock;
+    }
+
+
+private:
+    const int32_t    mFramesPerBlock = 1;
+    float           *mSampleBlock = nullptr; // allocated in constructor
+};
+
+/***************************************************************************/
+/**
+  * The results of a module are stored in the buffer of the output ports.
+  */
+class AudioFloatOutputPort : public AudioFloatBlockPort {
+public:
+    AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+            : AudioFloatBlockPort(parent, samplesPerFrame) {
+    }
+
+    virtual ~AudioFloatOutputPort() = default;
+
+    using AudioFloatBlockPort::getBlock;
+
+    /**
+     * Call the parent module's onProcess() method.
+     * That may pull data from its inputs and recursively
+     * process the entire graph.
+     * @return number of frames actually pulled
+     */
+    int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+    /**
+     * Connect to the input of another module.
+     * An input port can only have one connection.
+     * An output port can have multiple connections.
+     * If you connect a second output port to an input port
+     * then it overwrites the previous connection.
+     *
+     * This not thread safe. Do not modify the graph topology form another thread while running.
+     */
+    void connect(AudioFloatInputPort *port);
+
+    /**
+     * Disconnect from the input of another module.
+     * This not thread safe.
+     */
+    void disconnect(AudioFloatInputPort *port);
+};
+
+/***************************************************************************/
+class AudioFloatInputPort : public AudioFloatBlockPort {
+public:
+    AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+            : AudioFloatBlockPort(parent, samplesPerFrame) {
+    }
+
+    virtual ~AudioFloatInputPort() = default;
+
+    /**
+     * If connected to an output port then this will return
+     * that output ports buffers.
+     * If not connected then it returns the input ports own buffer
+     * which can be loaded using setValue().
+     */
+    float *getBlock() override;
+
+    /**
+     * Pull data from any output port that is connected.
+     */
+    int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+    /**
+     * Write every value of the float buffer.
+     * This value will be ignored if an output port is connected
+     * to this port.
+     */
+    void setValue(float value) {
+        int numFloats = kDefaultBlockSize * getSamplesPerFrame();
+        float *buffer = getBlock();
+        for (int i = 0; i < numFloats; i++) {
+            *buffer++ = value;
+        }
+    }
+
+    /**
+     * Connect to the output of another module.
+     * An input port can only have one connection.
+     * An output port can have multiple connections.
+     * This not thread safe.
+     */
+    void connect(AudioFloatOutputPort *port) {
+        assert(getSamplesPerFrame() == port->getSamplesPerFrame());
+        mConnected = port;
+    }
+
+    void disconnect(AudioFloatOutputPort *port) {
+        assert(mConnected == port);
+        (void) port;
+        mConnected = nullptr;
+    }
+
+    void disconnect() {
+        mConnected = nullptr;
+    }
+
+private:
+    AudioFloatOutputPort *mConnected = nullptr;
+};
+
+/***************************************************************************/
+class AudioSource : public AudioProcessorBase {
+public:
+    explicit AudioSource(int32_t channelCount)
+            : output(*this, channelCount) {
+    }
+
+    virtual ~AudioSource() = default;
+
+    AudioFloatOutputPort output;
+
+    void setData(const void *data, int32_t numFrames) {
+        mData = data;
+        mSizeInFrames = numFrames;
+        mFrameIndex = 0;
+    }
+
+protected:
+    const void *mData = nullptr;
+    int32_t     mSizeInFrames = 0; // number of frames in mData
+    int32_t     mFrameIndex = 0; // index of next frame to be processed
+};
+
+/***************************************************************************/
+class AudioSink : public AudioProcessorBase {
+public:
+    explicit AudioSink(int32_t channelCount)
+            : input(*this, channelCount) {
+    }
+
+    virtual ~AudioSink() = default;
+
+    AudioFloatInputPort input;
+
+    /**
+     * Dummy processor. The work happens in the read() method.
+     *
+     * @param framePosition index of first frame to be processed
+     * @param numFrames
+     * @return number of frames actually processed
+     */
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override {
+        (void) framePosition;
+        (void) numFrames;
+        return 0;
+    };
+
+    virtual int32_t read(void *data, int32_t numFrames) = 0;
+
+protected:
+    int32_t pull(int32_t numFrames);
+
+private:
+    int64_t mFramePosition = 0;
+};
+
+} /* namespace flowgraph */
+
+#endif /* FLOWGRAPH_AUDIO_PROCESSOR_BASE_H */
diff --git a/media/libaaudio/src/flowgraph/ClipToRange.cpp b/media/libaaudio/src/flowgraph/ClipToRange.cpp
new file mode 100644
index 0000000..bd9c22a
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/ClipToRange.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "ClipToRange.h"
+
+using namespace flowgraph;
+
+ClipToRange::ClipToRange(int32_t channelCount)
+        : input(*this, channelCount)
+        , output(*this, channelCount) {
+}
+
+int32_t ClipToRange::onProcess(int64_t framePosition, int32_t numFrames) {
+    int32_t framesToProcess = input.pullData(framePosition, numFrames);
+    const float *inputBuffer = input.getBlock();
+    float *outputBuffer = output.getBlock();
+
+    int32_t numSamples = framesToProcess * output.getSamplesPerFrame();
+    for (int32_t i = 0; i < numSamples; i++) {
+        *outputBuffer++ = std::min(mMaximum, std::max(mMinimum, *inputBuffer++));
+    }
+
+    return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/ClipToRange.h b/media/libaaudio/src/flowgraph/ClipToRange.h
new file mode 100644
index 0000000..9eef254
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/ClipToRange.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_CLIP_TO_RANGE_H
+#define FLOWGRAPH_CLIP_TO_RANGE_H
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
+// It is designed to allow occasional transient peaks.
+constexpr float kDefaultMaxHeadroom = 1.41253754f;
+constexpr float kDefaultMinHeadroom = -kDefaultMaxHeadroom;
+
+class ClipToRange : public AudioProcessorBase {
+public:
+    explicit ClipToRange(int32_t channelCount);
+
+    virtual ~ClipToRange() = default;
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+    void setMinimum(float min) {
+        mMinimum = min;
+    }
+
+    float getMinimum() const {
+        return mMinimum;
+    }
+
+    void setMaximum(float min) {
+        mMaximum = min;
+    }
+
+    float getMaximum() const {
+        return mMaximum;
+    }
+
+    AudioFloatInputPort input;
+    AudioFloatOutputPort output;
+
+private:
+    float mMinimum = kDefaultMinHeadroom;
+    float mMaximum = kDefaultMaxHeadroom;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_CLIP_TO_RANGE_H
diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp
new file mode 100644
index 0000000..78aad52
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "MonoToMultiConverter.h"
+
+using namespace flowgraph;
+
+MonoToMultiConverter::MonoToMultiConverter(int32_t channelCount)
+        : input(*this, 1)
+        , output(*this, channelCount) {
+}
+
+MonoToMultiConverter::~MonoToMultiConverter() { }
+
+int32_t MonoToMultiConverter::onProcess(int64_t framePosition, int32_t numFrames) {
+    int32_t framesToProcess = input.pullData(framePosition, numFrames);
+
+    const float *inputBuffer = input.getBlock();
+    float *outputBuffer = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+    // TODO maybe move to audio_util as audio_mono_to_multi()
+    for (int i = 0; i < framesToProcess; i++) {
+        // read one, write many
+        float sample = *inputBuffer++;
+        for (int channel = 0; channel < channelCount; channel++) {
+            *outputBuffer++ = sample;
+        }
+    }
+    return framesToProcess;
+}
+
diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.h b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h
new file mode 100644
index 0000000..34d53c7
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
+#define FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class MonoToMultiConverter : public AudioProcessorBase {
+public:
+    explicit MonoToMultiConverter(int32_t channelCount);
+
+    virtual ~MonoToMultiConverter();
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+    AudioFloatInputPort input;
+    AudioFloatOutputPort output;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
diff --git a/media/libaaudio/src/flowgraph/RampLinear.cpp b/media/libaaudio/src/flowgraph/RampLinear.cpp
new file mode 100644
index 0000000..a260828
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/RampLinear.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "RampLinear"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "RampLinear.h"
+
+using namespace flowgraph;
+
+RampLinear::RampLinear(int32_t channelCount)
+        : input(*this, channelCount)
+        , output(*this, channelCount) {
+    mTarget.store(1.0f);
+}
+
+void RampLinear::setLengthInFrames(int32_t frames) {
+    mLengthInFrames = frames;
+}
+
+void RampLinear::setTarget(float target) {
+    mTarget.store(target);
+}
+
+float RampLinear::interpolateCurrent() {
+    return mLevelTo - (mRemaining * mScaler);
+}
+
+int32_t RampLinear::onProcess(int64_t framePosition, int32_t numFrames) {
+    int32_t framesToProcess = input.pullData(framePosition, numFrames);
+    const float *inputBuffer = input.getBlock();
+    float *outputBuffer = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    float target = getTarget();
+    if (target != mLevelTo) {
+        // Start new ramp. Continue from previous level.
+        mLevelFrom = interpolateCurrent();
+        mLevelTo = target;
+        mRemaining = mLengthInFrames;
+        ALOGV("%s() mLevelFrom = %f, mLevelTo = %f, mRemaining = %d, mScaler = %f",
+              __func__, mLevelFrom, mLevelTo, mRemaining, mScaler);
+        mScaler = (mLevelTo - mLevelFrom) / mLengthInFrames; // for interpolation
+    }
+
+    int32_t framesLeft = framesToProcess;
+
+    if (mRemaining > 0) { // Ramping? This doesn't happen very often.
+        int32_t framesToRamp = std::min(framesLeft, mRemaining);
+        framesLeft -= framesToRamp;
+        while (framesToRamp > 0) {
+            float currentLevel = interpolateCurrent();
+            for (int ch = 0; ch < channelCount; ch++) {
+                *outputBuffer++ = *inputBuffer++ * currentLevel;
+            }
+            mRemaining--;
+            framesToRamp--;
+        }
+    }
+
+    // Process any frames after the ramp.
+    int32_t samplesLeft = framesLeft * channelCount;
+    for (int i = 0; i < samplesLeft; i++) {
+        *outputBuffer++ = *inputBuffer++ * mLevelTo;
+    }
+
+    return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/RampLinear.h b/media/libaaudio/src/flowgraph/RampLinear.h
new file mode 100644
index 0000000..bdc8f41
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/RampLinear.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_RAMP_LINEAR_H
+#define FLOWGRAPH_RAMP_LINEAR_H
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class RampLinear : public AudioProcessorBase {
+public:
+    explicit RampLinear(int32_t channelCount);
+
+    virtual ~RampLinear() = default;
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+    /**
+     * This is used for the next ramp.
+     * Calling this does not affect a ramp that is in progress.
+     */
+    void setLengthInFrames(int32_t frames);
+
+    int32_t getLengthInFrames() const {
+        return mLengthInFrames;
+    }
+
+    /**
+     * This may be safely called by another thread.
+     * @param target
+     */
+    void setTarget(float target);
+
+    float getTarget() const {
+        return mTarget.load();
+    }
+
+    /**
+     * Force the nextSegment to start from this level.
+     *
+     * WARNING: this can cause a discontinuity if called while the ramp is being used.
+     * Only call this when setting the initial ramp.
+     *
+     * @param level
+     */
+    void forceCurrent(float level) {
+        mLevelFrom = level;
+        mLevelTo = level;
+    }
+
+    AudioFloatInputPort input;
+    AudioFloatOutputPort output;
+
+private:
+
+    float interpolateCurrent();
+
+    std::atomic<float>  mTarget;
+
+    int32_t             mLengthInFrames  = 48000.0f / 100.0f ; // 10 msec at 48000 Hz;
+    int32_t             mRemaining       = 0;
+    float               mScaler          = 0.0f;
+    float               mLevelFrom       = 0.0f;
+    float               mLevelTo         = 0.0f;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_RAMP_LINEAR_H
diff --git a/media/libaaudio/src/flowgraph/SinkFloat.cpp b/media/libaaudio/src/flowgraph/SinkFloat.cpp
new file mode 100644
index 0000000..fb3dcbc
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkFloat.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "SinkFloat.h"
+
+using namespace flowgraph;
+
+SinkFloat::SinkFloat(int32_t channelCount)
+        : AudioSink(channelCount) {
+}
+
+int32_t SinkFloat::read(void *data, int32_t numFrames) {
+    float *floatData = (float *) data;
+    int32_t channelCount = input.getSamplesPerFrame();
+
+    int32_t framesLeft = numFrames;
+    while (framesLeft > 0) {
+        // Run the graph and pull data through the input port.
+        int32_t framesRead = pull(framesLeft);
+        if (framesRead <= 0) {
+            break;
+        }
+        const float *signal = input.getBlock();
+        int32_t numSamples = framesRead * channelCount;
+        memcpy(floatData, signal, numSamples * sizeof(float));
+        floatData += numSamples;
+        framesLeft -= framesRead;
+    }
+    return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkFloat.h b/media/libaaudio/src/flowgraph/SinkFloat.h
new file mode 100644
index 0000000..7775c08
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkFloat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_SINK_FLOAT_H
+#define FLOWGRAPH_SINK_FLOAT_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkFloat : public AudioSink {
+public:
+    explicit SinkFloat(int32_t channelCount);
+
+    int32_t read(void *data, int32_t numFrames) override;
+
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_FLOAT_H
diff --git a/media/libaaudio/src/flowgraph/SinkI16.cpp b/media/libaaudio/src/flowgraph/SinkI16.cpp
new file mode 100644
index 0000000..ffec8f5
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI16.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SinkI16.h"
+
+using namespace flowgraph;
+
+SinkI16::SinkI16(int32_t channelCount)
+        : AudioSink(channelCount) {}
+
+int32_t SinkI16::read(void *data, int32_t numFrames) {
+    int16_t *shortData = (int16_t *) data;
+    const int32_t channelCount = input.getSamplesPerFrame();
+
+    int32_t framesLeft = numFrames;
+    while (framesLeft > 0) {
+        // Run the graph and pull data through the input port.
+        int32_t framesRead = pull(framesLeft);
+        if (framesRead <= 0) {
+            break;
+        }
+        const float *signal = input.getBlock();
+        int32_t numSamples = framesRead * channelCount;
+#ifdef __ANDROID__
+        memcpy_to_i16_from_float(shortData, signal, numSamples);
+        shortData += numSamples;
+        signal += numSamples;
+#else
+        for (int i = 0; i < numSamples; i++) {
+            int32_t n = (int32_t) (*signal++ * 32768.0f);
+            *shortData++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
+        }
+#endif
+        framesLeft -= framesRead;
+    }
+    return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI16.h b/media/libaaudio/src/flowgraph/SinkI16.h
new file mode 100644
index 0000000..6d86266
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI16.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_SINK_I16_H
+#define FLOWGRAPH_SINK_I16_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkI16 : public AudioSink {
+public:
+    explicit SinkI16(int32_t channelCount);
+
+    int32_t read(void *data, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_I16_H
diff --git a/media/libaaudio/src/flowgraph/SinkI24.cpp b/media/libaaudio/src/flowgraph/SinkI24.cpp
new file mode 100644
index 0000000..6592828
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI24.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SinkI24.h"
+
+using namespace flowgraph;
+
+
+SinkI24::SinkI24(int32_t channelCount)
+        : AudioSink(channelCount) {}
+
+int32_t SinkI24::read(void *data, int32_t numFrames) {
+    uint8_t *byteData = (uint8_t *) data;
+    const int32_t channelCount = input.getSamplesPerFrame();
+
+    int32_t framesLeft = numFrames;
+    while (framesLeft > 0) {
+        // Run the graph and pull data through the input port.
+        int32_t framesRead = pull(framesLeft);
+        if (framesRead <= 0) {
+            break;
+        }
+        const float *floatData = input.getBlock();
+        int32_t numSamples = framesRead * channelCount;
+#ifdef __ANDROID__
+        memcpy_to_p24_from_float(byteData, floatData, numSamples);
+        static const int kBytesPerI24Packed = 3;
+        byteData += numSamples * kBytesPerI24Packed;
+        floatData += numSamples;
+#else
+        const int32_t kI24PackedMax = 0x007FFFFF;
+        const int32_t kI24PackedMin = 0xFF800000;
+        for (int i = 0; i < numSamples; i++) {
+            int32_t n = (int32_t) (*floatData++ * 0x00800000);
+            n = std::min(kI24PackedMax, std::max(kI24PackedMin, n)); // clip
+            // Write as a packed 24-bit integer in Little Endian format.
+            *byteData++ = (uint8_t) n;
+            *byteData++ = (uint8_t) (n >> 8);
+            *byteData++ = (uint8_t) (n >> 16);
+        }
+#endif
+        framesLeft -= framesRead;
+    }
+    return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI24.h b/media/libaaudio/src/flowgraph/SinkI24.h
new file mode 100644
index 0000000..5b9b505
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI24.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_SINK_I24_H
+#define FLOWGRAPH_SINK_I24_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkI24 : public AudioSink {
+public:
+    explicit SinkI24(int32_t channelCount);
+
+    int32_t read(void *data, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_I24_H
diff --git a/media/libaaudio/src/flowgraph/SourceFloat.cpp b/media/libaaudio/src/flowgraph/SourceFloat.cpp
new file mode 100644
index 0000000..4bb674f
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceFloat.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "SourceFloat.h"
+
+using namespace flowgraph;
+
+SourceFloat::SourceFloat(int32_t channelCount)
+        : AudioSource(channelCount) {
+}
+
+int32_t SourceFloat::onProcess(int64_t framePosition, int32_t numFrames) {
+
+    float *outputBuffer = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    int32_t framesLeft = mSizeInFrames - mFrameIndex;
+    int32_t framesToProcess = std::min(numFrames, framesLeft);
+    int32_t numSamples = framesToProcess * channelCount;
+
+    const float *floatBase = (float *) mData;
+    const float *floatData = &floatBase[mFrameIndex * channelCount];
+    memcpy(outputBuffer, floatData, numSamples * sizeof(float));
+    mFrameIndex += framesToProcess;
+    return framesToProcess;
+}
+
diff --git a/media/libaaudio/src/flowgraph/SourceFloat.h b/media/libaaudio/src/flowgraph/SourceFloat.h
new file mode 100644
index 0000000..e6eed9f
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceFloat.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_SOURCE_FLOAT_H
+#define FLOWGRAPH_SOURCE_FLOAT_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceFloat : public AudioSource {
+public:
+    explicit SourceFloat(int32_t channelCount);
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_FLOAT_H
diff --git a/media/libaaudio/src/flowgraph/SourceI16.cpp b/media/libaaudio/src/flowgraph/SourceI16.cpp
new file mode 100644
index 0000000..c3fcec2
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI16.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SourceI16.h"
+
+using namespace flowgraph;
+
+SourceI16::SourceI16(int32_t channelCount)
+        : AudioSource(channelCount) {
+}
+
+int32_t SourceI16::onProcess(int64_t framePosition, int32_t numFrames) {
+    float *floatData = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    int32_t framesLeft = mSizeInFrames - mFrameIndex;
+    int32_t framesToProcess = std::min(numFrames, framesLeft);
+    int32_t numSamples = framesToProcess * channelCount;
+
+    const int16_t *shortBase = static_cast<const int16_t *>(mData);
+    const int16_t *shortData = &shortBase[mFrameIndex * channelCount];
+
+#ifdef __ANDROID__
+    memcpy_to_float_from_i16(floatData, shortData, numSamples);
+#else
+    for (int i = 0; i < numSamples; i++) {
+        *floatData++ = *shortData++ * (1.0f / 32768);
+    }
+#endif
+
+    mFrameIndex += framesToProcess;
+    return framesToProcess;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/SourceI16.h b/media/libaaudio/src/flowgraph/SourceI16.h
new file mode 100644
index 0000000..2b116cf
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI16.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_SOURCE_I16_H
+#define FLOWGRAPH_SOURCE_I16_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceI16 : public AudioSource {
+public:
+    explicit SourceI16(int32_t channelCount);
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I16_H
diff --git a/media/libaaudio/src/flowgraph/SourceI24.cpp b/media/libaaudio/src/flowgraph/SourceI24.cpp
new file mode 100644
index 0000000..f319880
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI24.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SourceI24.h"
+
+using namespace flowgraph;
+
+constexpr int kBytesPerI24Packed = 3;
+
+SourceI24::SourceI24(int32_t channelCount)
+        : AudioSource(channelCount) {
+}
+
+int32_t SourceI24::onProcess(int64_t framePosition, int32_t numFrames) {
+    float *floatData = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    int32_t framesLeft = mSizeInFrames - mFrameIndex;
+    int32_t framesToProcess = std::min(numFrames, framesLeft);
+    int32_t numSamples = framesToProcess * channelCount;
+
+    const uint8_t *byteBase = (uint8_t *) mData;
+    const uint8_t *byteData = &byteBase[mFrameIndex * channelCount * kBytesPerI24Packed];
+
+#ifdef __ANDROID__
+    memcpy_to_float_from_p24(floatData, byteData, numSamples);
+#else
+    static const float scale = 1. / (float)(1UL << 31);
+    for (int i = 0; i < numSamples; i++) {
+        // Assemble the data assuming Little Endian format.
+        int32_t pad = byteData[2];
+        pad <<= 8;
+        pad |= byteData[1];
+        pad <<= 8;
+        pad |= byteData[0];
+        pad <<= 8; // Shift to 32 bit data so the sign is correct.
+        byteData += kBytesPerI24Packed;
+        *floatData++ = pad * scale; // scale to range -1.0 to 1.0
+    }
+#endif
+
+    mFrameIndex += framesToProcess;
+    return framesToProcess;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/SourceI24.h b/media/libaaudio/src/flowgraph/SourceI24.h
new file mode 100644
index 0000000..39f14da
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI24.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 FLOWGRAPH_SOURCE_I24_H
+#define FLOWGRAPH_SOURCE_I24_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceI24 : public AudioSource {
+public:
+    explicit SourceI24(int32_t channelCount);
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I24_H