OboeTester: use new flowgraph API with simpler pullData
diff --git a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
index 2a3e204..6d21ebe 100644
--- a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
+++ b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.cpp
@@ -17,7 +17,6 @@
 #include <cstring>
 #include <sched.h>
 
-#include "common/OboeDebug.h"
 #include "oboe/Oboe.h"
 #include "AudioStreamGateway.h"
 
@@ -31,6 +30,8 @@
 {
 }
 
+int64_t AudioStreamGateway::mFramePosition = 0;
+
 oboe::DataCallbackResult AudioStreamGateway::onAudioReady(
         oboe::AudioStream *audioStream,
         void *audioData,
@@ -42,7 +43,8 @@
     }
 
     if (mAudioSink != nullptr) {
-        mAudioSink->read(audioData, numFrames);
+        mAudioSink->read(mFramePosition, audioData, numFrames);
+        mFramePosition += numFrames;
     }
 
     return oboe::DataCallbackResult::Continue;
diff --git a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
index 09d69c6..8eda5d1 100644
--- a/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
+++ b/apps/OboeTester/app/src/main/cpp/AudioStreamGateway.h
@@ -49,7 +49,10 @@
     int getScheduler();
 
 private:
-    // TODO uint64_t mFramePosition;
+    // Use a static position so that it will monotonically increase.
+    // Note only one gateway can be used.
+    // TODO remove single gateway limitation. Add Graph class.
+    static int64_t mFramePosition;
     bool     mSchedulerChecked = false;
     int      mScheduler;
     std::shared_ptr<flowgraph::AudioSink>  mAudioSink;
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
index d3c5ebe..a9e3501 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
@@ -78,35 +78,35 @@
 }
 
 void NativeAudioContext::connectTone() {
-    if (monoToMulti != nullptr) {
-        LOGI("%s() mToneType = %d", __func__, mToneType);
-        switch (mToneType) {
-            case ToneType::SawPing:
-                sawPingGenerator.output.connect(&(monoToMulti->input));
-                monoToMulti->output.connect(&(mSinkFloat.get()->input));
-                monoToMulti->output.connect(&(mSinkI16.get()->input));
-                break;
-            case ToneType::Sine:
-                for (int i = 0; i < mChannelCount; i++) {
-                    sineOscillators[i].output.connect(manyToMulti->inputs[i].get());
-                }
-                manyToMulti->output.connect(&(mSinkFloat.get()->input));
-                manyToMulti->output.connect(&(mSinkI16.get()->input));
-                break;
-            case ToneType::Impulse:
-                impulseGenerator.output.connect(&(monoToMulti->input));
-                monoToMulti->output.connect(&(mSinkFloat.get()->input));
-                monoToMulti->output.connect(&(mSinkI16.get()->input));
-                break;
-            case ToneType::Sawtooth:
-                for (int i = 0; i < mChannelCount; i++) {
-                    sawtoothOscillators[i].output.connect(manyToMulti->inputs[i].get());
-                }
-                manyToMulti->output.connect(&(mSinkFloat.get()->input));
-                manyToMulti->output.connect(&(mSinkI16.get()->input));
-                break;
-        }
-    }
+//    if (monoToMulti != nullptr) {
+//        LOGI("%s() mToneType = %d", __func__, mToneType);
+//        switch (mToneType) {
+//            case ToneType::SawPing:
+//                sawPingGenerator.output.connect(&(monoToMulti->input));
+//                monoToMulti->output.connect(&(mSinkFloat.get()->input));
+//                monoToMulti->output.connect(&(mSinkI16.get()->input));
+//                break;
+//            case ToneType::Sine:
+//                for (int i = 0; i < mChannelCount; i++) {
+//                    sineOscillators[i].output.connect(manyToMulti->inputs[i].get());
+//                }
+//                manyToMulti->output.connect(&(mSinkFloat.get()->input));
+//                manyToMulti->output.connect(&(mSinkI16.get()->input));
+//                break;
+//            case ToneType::Impulse:
+//                impulseGenerator.output.connect(&(monoToMulti->input));
+//                monoToMulti->output.connect(&(mSinkFloat.get()->input));
+//                monoToMulti->output.connect(&(mSinkI16.get()->input));
+//                break;
+//            case ToneType::Sawtooth:
+//                for (int i = 0; i < mChannelCount; i++) {
+//                    sawtoothOscillators[i].output.connect(manyToMulti->inputs[i].get());
+//                }
+//                manyToMulti->output.connect(&(mSinkFloat.get()->input));
+//                manyToMulti->output.connect(&(mSinkI16.get()->input));
+//                break;
+//        }
+//    }
 }
 
 void NativeAudioContext::setChannelEnabled(int channelIndex, bool enabled) {
@@ -191,6 +191,8 @@
 
     // We needed the proxy because we did not know the channelCount when we setup the Builder.
     if (useCallback) {
+        LOGD("NativeAudioContext::open() set callback to use oboeCallbackProxy, size = %d",
+             callbackSize);
         builder.setCallback(&oboeCallbackProxy);
         builder.setFramesPerCallback(callbackSize);
     }
@@ -232,9 +234,27 @@
     return nullptr;
 }
 
+oboe::AudioStream * NativeAudioContext::getInputStream() {
+    for (int32_t i = 0; i < kMaxStreams; i++) {
+        oboe::AudioStream *oboeStream = mOboeStreams[i];
+        if (oboeStream != nullptr) {
+            if (oboeStream->getDirection() == oboe::Direction::Input) {
+                return oboeStream;
+            }
+        }
+    }
+    return nullptr;
+}
+
 void NativeAudioContext::configureForActivityType() {
     oboe::AudioStream *outputStream = nullptr;
 
+    manyToMulti = std::make_unique<ManyToMultiConverter>(mChannelCount);
+    monoToMulti = std::make_unique<MonoToMultiConverter>(mChannelCount);
+
+    mSinkFloat = std::make_unique<SinkFloat>(mChannelCount);
+    mSinkI16 = std::make_unique<SinkI16>(mChannelCount);
+
     switch(mActivityType) {
         case ActivityType::Undefined:
             break;
@@ -258,18 +278,12 @@
                     sineOscillators[i].frequency.setValue(frequency);
                     frequency *= 4.0 / 3.0; // each sine is at a higher frequency
                     sineOscillators[i].amplitude.setValue(AMPLITUDE_SINE);
-                }
-                for (int i = 0; i < mChannelCount; i++) {
-                    sawtoothOscillators[i].setSampleRate(outputStream->getSampleRate());
-                    sawtoothOscillators[i].frequency.setValue(frequency);
-                    frequency *= 4.0 / 3.0; // each sawtooth is at a higher frequency
-                    sawtoothOscillators[i].amplitude.setValue(AMPLITUDE_SAWTOOTH);
+                    sineOscillators[i].output.connect(manyToMulti->inputs[i].get());
                 }
             }
 
-            impulseGenerator.setSampleRate(outputStream->getSampleRate());
-            impulseGenerator.frequency.setValue(440.0);
-            impulseGenerator.amplitude.setValue(AMPLITUDE_IMPULSE);
+            manyToMulti->output.connect(&(mSinkFloat.get()->input));
+            manyToMulti->output.connect(&(mSinkI16.get()->input));
             break;
 
         case ActivityType::TapToTone:
@@ -277,6 +291,12 @@
             sawPingGenerator.setSampleRate(outputStream->getSampleRate());
             sawPingGenerator.frequency.setValue(FREQUENCY_SAW_PING);
             sawPingGenerator.amplitude.setValue(AMPLITUDE_SAW_PING);
+
+            sawPingGenerator.output.connect(&(monoToMulti->input));
+            monoToMulti->output.connect(&(mSinkFloat.get()->input));
+            monoToMulti->output.connect(&(mSinkI16.get()->input));
+
+            sawPingGenerator.setEnabled(false);
             break;
 
         case ActivityType::Echo:
@@ -284,11 +304,9 @@
     }
 
     if (outputStream != nullptr) {
-        manyToMulti = std::make_unique<ManyToMultiConverter>(mChannelCount);
-        monoToMulti = std::make_unique<MonoToMultiConverter>(mChannelCount);
 
-        mSinkFloat = std::make_unique<SinkFloat>(mChannelCount);
-        mSinkI16 = std::make_unique<SinkI16>(mChannelCount);
+        mSinkFloat->start();
+        mSinkI16->start();
 
         // We needed the proxy because we did not know the channelCount
         // when we setup the Builder.
@@ -299,8 +317,6 @@
             audioStreamGateway->setAudioSink(mSinkFloat);
         }
 
-        connectTone();
-
         if (useCallback) {
             oboeCallbackProxy.setCallback(audioStreamGateway.get());
         }
@@ -322,6 +338,56 @@
     }
 }
 
+oboe::Result NativeAudioContext::start() {
+    oboe::Result result = oboe::Result::OK;
+    oboe::AudioStream *inputStream = getInputStream();
+    oboe::AudioStream *outputStream = getOutputStream();
+    if (inputStream == nullptr && outputStream == nullptr) {
+        return oboe::Result::ErrorInvalidState; // not open
+    }
+
+    stop();
+
+    LOGD("NativeAudioContext: %s() called", __func__);
+    configureForActivityType();
+
+    switch(mActivityType) {
+        case ActivityType::Undefined:
+            break;
+        case ActivityType::TestInput:
+        case ActivityType::RecordPlay:
+            result = inputStream->requestStart();
+            if (!useCallback && result == oboe::Result::OK) {
+                LOGD("start thread for blocking I/O");
+                // Instead of using the callback, start a thread that readsthe stream.
+                threadEnabled.store(true);
+                dataThread = new std::thread(threadCallback, this);
+            }
+            break;
+
+        case ActivityType::TestOutput:
+        case ActivityType::TapToTone:
+            result = outputStream->requestStart();
+            if (!useCallback && result == oboe::Result::OK) {
+                LOGD("start thread for blocking I/O");
+                // Instead of using the callback, start a thread that writes the stream.
+                threadEnabled.store(true);
+                dataThread = new std::thread(threadCallback, this);
+            }
+            break;
+
+        case ActivityType::Echo:
+            inputStream = getInputStream();
+            outputStream = getOutputStream();
+            result = inputStream->requestStart(); // FIXME use full duplex object
+            result = outputStream->requestStart();
+            break;
+    }
+
+    LOGD("OboeAudioStream_start: start returning %d", result);
+    return result;
+}
+
 void NativeAudioContext::runBlockingIO() {
     int32_t framesPerBlock = getFramesPerBlock();
     oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Continue;
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
index 1fc41be..718ae1c 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
@@ -89,7 +89,7 @@
     void setToneType(int toneType) {
         LOGI("%s(%d)", __func__, toneType);
         mToneType = (ToneType) toneType;
-        connectTone();
+        // connectTone();
     }
 
     int32_t getFramesPerBlock() {
@@ -103,7 +103,6 @@
         }
     }
 
-
     oboe::Result pause() {
         LOGD("NativeAudioContext::%s() called", __func__);
         oboe::Result result = oboe::Result::OK;
@@ -203,61 +202,11 @@
         LOGD("%s: exiting", __func__);
     }
 
-    oboe::Result start() {
-
-        LOGD("NativeAudioContext: %s() called", __func__);
-        configureForActivityType();
-
-        bool gotOne = false;
-        for (int32_t i = 0; i < kMaxStreams; i++) {
-            gotOne = (mOboeStreams[i] != nullptr);
-            if (gotOne) break;
-        }
-        if (!gotOne) {
-            LOGD("NativeAudioContext: %s() did not find a stream", __func__);
-            return oboe::Result::ErrorNull;
-        }
-
-        stop();
-
-        LOGD("NativeAudioContext: %s() start modules", __func__);
-        for (int i = 0; i < mChannelCount; i++) {
-            sineOscillators[i].start();
-            sawtoothOscillators[i].start();
-        }
-        impulseGenerator.start();
-        sawPingGenerator.start();
-        if (mSinkFloat) {
-            mSinkFloat->start();
-        }
-        if (mSinkI16) {
-            mSinkI16->start();
-        }
-
-        LOGD("NativeAudioContext: %s start stream", __func__);
-        oboe::Result result = oboe::Result::OK;
-        for (int32_t i = 0; i < kMaxStreams; i++) {
-            oboe::AudioStream *oboeStream = mOboeStreams[i];
-            if (oboeStream != nullptr) {
-                result = oboeStream->requestStart();
-
-                if (!useCallback && result == oboe::Result::OK) {
-                    LOGD("OboeAudioStream_start: start thread for blocking I/O");
-                    // Instead of using the callback, start a thread that reads or writes the stream. // FIXME
-                    threadEnabled.store(true);
-                    dataThread = new std::thread(threadCallback, this);
-                }
-            }
-        }
-        LOGD("OboeAudioStream_start: start returning %d", result);
-        return result;
-    }
+    oboe::Result start();
 
     void setToneEnabled(bool enabled) {
         LOGD("%s(%d)", __func__, enabled ? 1 : 0);
-        // sineGenerator.setEnabled(enabled); // not needed
         sawPingGenerator.setEnabled(enabled);
-        // impulseGenerator.setEnabled(enabled); // not needed
     }
 
     void setAmplitude(double amplitude) {
@@ -310,6 +259,7 @@
         Echo = 4
     };
 
+    oboe::AudioStream * getInputStream();
     oboe::AudioStream * getOutputStream();
 
     int32_t allocateStreamIndex();
diff --git a/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp b/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp
index be08e78..6e3156a 100644
--- a/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp
+++ b/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "common/OboeDebug.h"
 #include "OboeStreamCallbackProxy.h"
 
 OboeStreamCallbackProxy::~OboeStreamCallbackProxy() {
@@ -30,6 +31,8 @@
     if (mCallback != nullptr) {
         return mCallback->onAudioReady(audioStream, audioData, numFrames);
     }
+    LOGD("OboeStreamCallbackProxy: %s() called", __func__);
+//    memset(audioData, 0, numFrames * audioStream->getChannelCount() * audioStream->getBytesPerSample()); // FIXME
     return oboe::DataCallbackResult::Stop;
 }
 
diff --git a/apps/OboeTester/app/src/main/cpp/SawPingGenerator.cpp b/apps/OboeTester/app/src/main/cpp/SawPingGenerator.cpp
index 22c80f9..64c2323 100644
--- a/apps/OboeTester/app/src/main/cpp/SawPingGenerator.cpp
+++ b/apps/OboeTester/app/src/main/cpp/SawPingGenerator.cpp
@@ -31,12 +31,7 @@
 
 SawPingGenerator::~SawPingGenerator() { }
 
-int32_t SawPingGenerator::onProcess(
-        int64_t framePosition,
-        int numFrames) {
-
-    frequency.pullData(framePosition, numFrames);
-    amplitude.pullData(framePosition, numFrames);
+int32_t SawPingGenerator::onProcess(int numFrames) {
 
     const float *frequencies = frequency.getBuffer();
     const float *amplitudes = amplitude.getBuffer();
@@ -69,6 +64,8 @@
     LOGD("%s(%d)", __func__, enabled ? 1 : 0);
     if (enabled) {
         mRequestCount++;
+    } else {
+        mAcknowledgeCount.store(mRequestCount.load());
     }
 }
 
diff --git a/apps/OboeTester/app/src/main/cpp/SawPingGenerator.h b/apps/OboeTester/app/src/main/cpp/SawPingGenerator.h
index 05bb745..49bfc2e 100644
--- a/apps/OboeTester/app/src/main/cpp/SawPingGenerator.h
+++ b/apps/OboeTester/app/src/main/cpp/SawPingGenerator.h
@@ -30,17 +30,10 @@
 
     virtual ~SawPingGenerator();
 
-    int32_t onProcess(
-            int64_t framePosition,
-            int numFrames) override;
+    int32_t onProcess(int numFrames) override;
 
     void setEnabled(bool enabled);
 
-    void start() override {
-        OscillatorBase::start();
-        mAcknowledgeCount.store(mRequestCount.load());
-    }
-
 private:
     std::atomic<int> mRequestCount; // external thread increments this to request a beep
     std::atomic<int> mAcknowledgeCount; // audio thread sets this to acknowledge