oboe: add blocking read/write

Use FIFO for OpenSL ES.
diff --git a/include/oboe/AudioStream.h b/include/oboe/AudioStream.h
index 90b98a8..86864da 100644
--- a/include/oboe/AudioStream.h
+++ b/include/oboe/AudioStream.h
@@ -151,9 +151,9 @@
      * This monotonic counter will never get reset.
      * @return the number of frames written so far
      */
-    virtual int64_t getFramesWritten() { return mFramesWritten; }
+    virtual int64_t getFramesWritten() const { return mFramesWritten; }
 
-    virtual int64_t getFramesRead() { return static_cast<int64_t>(Result::ErrorUnimplemented); }
+    virtual int64_t getFramesRead() const { return mFramesRead; }
 
     virtual Result getTimestamp(clockid_t clockId,
                                        int64_t *framePosition,
@@ -197,6 +197,9 @@
     virtual int64_t incrementFramesWritten(int32_t frames) {
         return mFramesWritten += frames;
     }
+    virtual int64_t incrementFramesRead(int32_t frames) {
+        return mFramesRead += frames;
+    }
 
     /**
      * Wait for a transition from one state to another.
@@ -219,7 +222,9 @@
     AudioFormat mNativeFormat = AudioFormat::Invalid;
 
 private:
+    // TODO these should be atomic like in AAudio
     int64_t              mFramesWritten = 0;
+    int64_t              mFramesRead = 0;
     int                  mPreviousScheduler = -1;
 };
 
diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp
index a256ef1..4f9a112 100644
--- a/src/aaudio/AudioStreamAAudio.cpp
+++ b/src/aaudio/AudioStreamAAudio.cpp
@@ -66,7 +66,6 @@
     }
 }
 
-
 namespace oboe {
 
 /*
@@ -282,6 +281,18 @@
     }
 }
 
+int32_t AudioStreamAAudio::read(void *buffer,
+                                 int32_t numFrames,
+                                 int64_t timeoutNanoseconds)
+{
+    AAudioStream *stream = mAAudioStream.load();
+    if (stream != nullptr) {
+        return mLibLoader->stream_read(mAAudioStream, buffer, numFrames, timeoutNanoseconds);
+    } else {
+        return static_cast<int32_t>(Result::ErrorNull);
+    }
+}
+
 Result AudioStreamAAudio::waitForStateChange(StreamState currentState,
                                         StreamState *nextState,
                                         int64_t timeoutNanoseconds)
@@ -339,8 +350,7 @@
     }
 }
 
-int64_t AudioStreamAAudio::getFramesRead()
-{
+int64_t AudioStreamAAudio::getFramesRead() const {
     AAudioStream *stream = mAAudioStream.load();
     if (stream != nullptr) {
         return mLibLoader->stream_getFramesRead(stream);
@@ -348,8 +358,8 @@
         return static_cast<int32_t>(Result::ErrorNull);
     }
 }
-int64_t AudioStreamAAudio::getFramesWritten()
-{
+
+int64_t AudioStreamAAudio::getFramesWritten() const {
     AAudioStream *stream = mAAudioStream.load();
     if (stream != nullptr) {
         return mLibLoader->stream_getFramesWritten(stream);
diff --git a/src/aaudio/AudioStreamAAudio.h b/src/aaudio/AudioStreamAAudio.h
index 9c5c406..a9f90fe 100644
--- a/src/aaudio/AudioStreamAAudio.h
+++ b/src/aaudio/AudioStreamAAudio.h
@@ -61,16 +61,20 @@
     Result requestStop() override;
 
     int32_t write(const void *buffer,
-                             int32_t numFrames,
-                             int64_t timeoutNanoseconds) override;
+                  int32_t numFrames,
+                  int64_t timeoutNanoseconds) override;
+
+    int32_t read(void *buffer,
+                 int32_t numFrames,
+                 int64_t timeoutNanoseconds) override;
 
     Result setBufferSizeInFrames(int32_t requestedFrames) override;
     int32_t getBufferSizeInFrames() const override;
     int32_t getFramesPerBurst() override;
     int32_t getXRunCount() override;
 
-    int64_t getFramesRead() override;
-    int64_t getFramesWritten() override;
+    int64_t getFramesRead() const override;
+    int64_t getFramesWritten() const override;
 
     Result waitForStateChange(StreamState currentState,
                               StreamState *nextState,
diff --git a/src/fifo/FifoBuffer.cpp b/src/fifo/FifoBuffer.cpp
index 030e57d..eb10d3f 100644
--- a/src/fifo/FifoBuffer.cpp
+++ b/src/fifo/FifoBuffer.cpp
@@ -17,6 +17,7 @@
 #include <stdint.h>
 #include <time.h>
 #include <memory.h>
+#include <assert.h>
 
 #include "common/OboeDebug.h"
 #include "fifo/FifoControllerBase.h"
@@ -36,12 +37,15 @@
         , mFramesUnderrunCount(0)
         , mUnderrunCount(0)
 {
+    assert(bytesPerFrame > 0);
+    assert(capacityInFrames > 0);
     mFifo = new FifoController(capacityInFrames, capacityInFrames);
     // allocate buffer
     int32_t bytesPerBuffer = bytesPerFrame * capacityInFrames;
     mStorage = new uint8_t[bytesPerBuffer];
     mStorageOwned = true;
-    LOGD("FifoProcessor: numFrames = %d, bytesPerFrame = %d", capacityInFrames, bytesPerFrame);
+    LOGD("FifoProcessor: capacityInFrames = %d, bytesPerFrame = %d",
+         capacityInFrames, bytesPerFrame);
 }
 
 FifoBuffer::FifoBuffer( uint32_t   bytesPerFrame,
@@ -64,7 +68,8 @@
                                        writeIndexAddress);
     mStorage = dataStorageAddress;
     mStorageOwned = false;
-    LOGD("FifoProcessor: capacityInFrames = %d, bytesPerFrame = %d", capacityInFrames, bytesPerFrame);
+    LOGD("FifoProcessor: capacityInFrames = %d, bytesPerFrame = %d",
+         capacityInFrames, bytesPerFrame);
 }
 
 FifoBuffer::~FifoBuffer() {
diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp
index 3ff10e6..0a316d8 100644
--- a/src/opensles/AudioInputStreamOpenSLES.cpp
+++ b/src/opensles/AudioInputStreamOpenSLES.cpp
@@ -139,6 +139,11 @@
         goto error;
     }
 
+    oboeResult = finishOpen();
+    if (oboeResult != oboe::Result::OK) {
+        goto error;
+    }
+
     return Result::OK;
 
 error:
diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp
index 2129766..c4ea4bb 100644
--- a/src/opensles/AudioOutputStreamOpenSLES.cpp
+++ b/src/opensles/AudioOutputStreamOpenSLES.cpp
@@ -141,6 +141,11 @@
         goto error;
     }
 
+    oboeResult = finishOpen();
+    if (oboeResult != oboe::Result::OK) {
+        goto error;
+    }
+
     return Result::OK;
 error:
     return Result::ErrorInternal; // TODO convert error from SLES to OBOE
diff --git a/src/opensles/AudioStreamBuffered.cpp b/src/opensles/AudioStreamBuffered.cpp
index ceb4c85..0b6d1a3 100644
--- a/src/opensles/AudioStreamBuffered.cpp
+++ b/src/opensles/AudioStreamBuffered.cpp
@@ -25,23 +25,18 @@
  * AudioStream with a FifoBuffer
  */
 AudioStreamBuffered::AudioStreamBuffered(const AudioStreamBuilder &builder)
-        : AudioStream(builder)
-        , mFifoBuffer(nullptr)
-{
+        : AudioStream(builder) {
+}
+AudioStreamBuffered::~AudioStreamBuffered() {
+    delete mFifoBuffer;
 }
 
-Result AudioStreamBuffered::open() {
-
-    Result result = AudioStream::open();
-    if (result != Result::OK) {
-        return result;
-    }
-
+Result AudioStreamBuffered::finishOpen() {
     // If the caller does not provide a callback use our own internal
     // callback that reads data from the FIFO.
     if (getCallback() == nullptr) {
-        LOGD("AudioStreamBuffered(): new FifoBuffer");
-        // TODO: Fix memory leak here
+        LOGD("AudioStreamBuffered(): new FifoBuffer(bytesPerFrame=%d", getBytesPerFrame());
+        // FIFO is configured with the same format and channels as the stream.
         mFifoBuffer = new FifoBuffer(getBytesPerFrame(), 1024); // TODO size?
         // Create a callback that reads from the FIFO
         mInternalCallback = std::unique_ptr<AudioStreamBufferedCallback>(new AudioStreamBufferedCallback(this));
@@ -51,17 +46,19 @@
     return Result::OK;
 }
 
-// TODO: This method should return a tuple of Result,int32_t where the 2nd return param is the frames written
+// TODO: This method should return a tuple of Result,int32_t where the
+// 2nd return param is the frames written. Maybe not!
 int32_t AudioStreamBuffered::write(const void *buffer,
-                              int32_t numFrames,
-                              int64_t timeoutNanoseconds)
+                                   int32_t numFrames,
+                                   int64_t timeoutNanoseconds)
 {
     int32_t result = 0;
     uint8_t *source = (uint8_t *)buffer;
     int32_t framesLeft = numFrames;
     while(framesLeft > 0 && result >= 0) {
-        result = mFifoBuffer->write(source, numFrames);
-        LOGD("AudioStreamBuffered::writeNow(): wrote %d/%d frames", result, numFrames);
+        result = mFifoBuffer->write(source, framesLeft);
+        LOGD("AudioStreamBuffered::%s(): wrote %d / %d frames to FIFO, [0] = %f",
+             __func__, result, framesLeft, ((float *)source)[0]);
         if (result > 0) {
             source += mFifoBuffer->convertFramesToBytes(result);
             incrementFramesWritten(result);
@@ -76,6 +73,35 @@
     return result;
 }
 
+// Read from the FIFO that was written by the callback.
+int32_t AudioStreamBuffered::read(void *buffer,
+                                  int32_t numFrames,
+                                  int64_t timeoutNanoseconds)
+{
+    static int readCount = 0;
+    int32_t result = 0;
+    uint8_t *destination = (uint8_t *)buffer;
+    int32_t framesLeft = numFrames;
+    while(framesLeft > 0 && result >= 0) {
+        result = mFifoBuffer->read(destination, framesLeft);
+        LOGD("AudioStreamBuffered::%s(): read %d/%d frames from FIFO, #%d",
+             __func__, result, framesLeft, readCount);
+        if (result > 0) {
+            destination += mFifoBuffer->convertFramesToBytes(result);
+            incrementFramesRead(result);
+            framesLeft -= result;
+            readCount++;
+        }
+        if (framesLeft > 0 && result >= 0) {
+            // FIXME use proper timing model, borrow one from AAudio
+            // TODO use timeoutNanoseconds
+            AudioClock::sleepForNanos(4 * kNanosPerMillisecond);
+        }
+    }
+
+    return result;
+}
+
 Result AudioStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames)
 {
     if (mFifoBuffer != nullptr) {
diff --git a/src/opensles/AudioStreamBuffered.h b/src/opensles/AudioStreamBuffered.h
index c4c2914..6789b6d 100644
--- a/src/opensles/AudioStreamBuffered.h
+++ b/src/opensles/AudioStreamBuffered.h
@@ -17,6 +17,8 @@
 #ifndef OBOE_STREAM_BUFFERED_H
 #define OBOE_STREAM_BUFFERED_H
 
+#include <cstring>
+#include <assert.h>
 #include "common/OboeDebug.h"
 #include "oboe/AudioStream.h"
 #include "oboe/AudioStreamCallback.h"
@@ -25,18 +27,24 @@
 namespace oboe {
 
 // A stream that contains a FIFO buffer.
+// This is used to implement blocking reads and writes.
 class AudioStreamBuffered : public AudioStream {
 public:
 
     AudioStreamBuffered();
     explicit AudioStreamBuffered(const AudioStreamBuilder &builder);
+    ~AudioStreamBuffered();
 
-    Result open() override;
+    Result finishOpen();
 
     int32_t write(const void *buffer,
                   int32_t numFrames,
                   int64_t timeoutNanoseconds) override;
 
+    int32_t read(void *buffer,
+                 int32_t numFrames,
+                 int64_t timeoutNanoseconds) override;
+
     Result setBufferSizeInFrames(int32_t requestedFrames) override;
 
     int32_t getBufferSizeInFrames() const override;
@@ -57,9 +65,23 @@
                 AudioStream *audioStream,
                 void *audioData,
                 int numFrames) {
-            int32_t framesRead = mBufferedStream->mFifoBuffer->readNow(audioData, numFrames);
-            //LOGD("AudioStreamBufferedCallback(): read %d / %d frames", framesRead, numFrames);
-            return (framesRead >= 0) ? DataCallbackResult::Continue : DataCallbackResult::Stop;
+            int32_t framesTransferred  = 0;
+            static int transferCount = 0;
+
+            if (mBufferedStream->getDirection() == oboe::Direction::Output) {
+                // This OUTPUT callback will read from the FIFO and write to audioData
+                framesTransferred = mBufferedStream->mFifoBuffer->readNow(audioData, numFrames);
+                LOGV("AudioStreamBufferedCallback::onAudioReady() read %d / %d frames from FIFO, #%d",
+                     framesTransferred, numFrames, transferCount);
+            } else {
+                // This INPUT callback will read from audioData and write to the FIFO
+                framesTransferred = mBufferedStream->mFifoBuffer->write(audioData, numFrames);
+                LOGD("AudioStreamBufferedCallback::onAudioReady() wrote %d / %d frames to FIFO, #%d",
+                     framesTransferred, numFrames, transferCount);
+            }
+            transferCount++;
+            // return (framesTransferred >= 0) ? DataCallbackResult::Continue : DataCallbackResult::Stop;
+            return DataCallbackResult::Continue;
         }
 
         virtual void onExit(Result reason) {}
@@ -69,7 +91,7 @@
 
 private:
 
-    FifoBuffer *mFifoBuffer;
+    FifoBuffer                                  *mFifoBuffer = nullptr;
     std::unique_ptr<AudioStreamBufferedCallback> mInternalCallback;
 };
 
diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp
index 3830eeb..80ef90d 100644
--- a/src/opensles/AudioStreamOpenSLES.cpp
+++ b/src/opensles/AudioStreamOpenSLES.cpp
@@ -74,6 +74,7 @@
     // API 21+: FLOAT
     // API <21: INT16
     if (mFormat == AudioFormat::Unspecified){
+        // TODO use runtime check
         mFormat = (__ANDROID_API__ < __ANDROID_API_L__) ?
                   AudioFormat::I16 : AudioFormat::Float;
     }