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;
}