aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp2
-rw-r--r--apps/OboeTester/app/src/main/cpp/NativeAudioContext.h7
-rw-r--r--apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp29
-rw-r--r--apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.h11
-rw-r--r--apps/OboeTester/app/src/main/cpp/jni-bridge.cpp8
-rw-r--r--apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/OboeAudioStream.java5
-rw-r--r--apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java25
-rw-r--r--include/oboe/AudioStream.h32
-rw-r--r--include/oboe/AudioStreamBase.h41
-rw-r--r--include/oboe/AudioStreamBuilder.h40
-rw-r--r--include/oboe/AudioStreamCallback.h90
-rw-r--r--src/aaudio/AudioStreamAAudio.cpp44
-rw-r--r--src/aaudio/AudioStreamAAudio.h5
-rw-r--r--src/common/AudioSourceCaller.cpp2
-rw-r--r--src/common/AudioStream.cpp6
-rw-r--r--src/common/DataConversionFlowGraph.cpp9
-rw-r--r--src/common/FilterAudioStream.cpp15
-rw-r--r--src/common/FilterAudioStream.h37
-rw-r--r--src/common/QuirksManager.cpp2
-rw-r--r--src/opensles/AudioStreamBuffered.cpp2
-rw-r--r--src/opensles/AudioStreamBuffered.h2
-rw-r--r--tests/testStreamClosedMethods.cpp12
22 files changed, 312 insertions, 114 deletions
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
index ea8b1228..7809206f 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
@@ -130,7 +130,7 @@ void ActivityContext::configureBuilder(bool isInput, oboe::AudioStreamBuilder &b
if (mUseCallback) {
LOGD("ActivityContext::open() set callback to use oboeCallbackProxy, callback size = %d",
callbackSize);
- builder.setCallback(&oboeCallbackProxy);
+ builder.setDataCallback(&oboeCallbackProxy);
builder.setFramesPerCallback(callbackSize);
}
}
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
index 50d0e90f..95df9849 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
@@ -192,6 +192,7 @@ class ActivityContext {
public:
ActivityContext() {}
+
virtual ~ActivityContext() = default;
oboe::AudioStream *getStream(int32_t streamIndex) {
@@ -288,7 +289,11 @@ public:
}
oboe::Result getLastErrorCallbackResult() {
- return oboeCallbackProxy.getLastErrorCallbackResult();
+ oboe::AudioStream *stream = getOutputStream();
+ if (stream == nullptr) {
+ stream = getInputStream();
+ }
+ return stream ? oboe::Result::ErrorNull : stream->getLastErrorCallbackResult();
}
int32_t getFramesPerCallback() {
diff --git a/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp b/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp
index f16e861c..a17575bf 100644
--- a/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp
+++ b/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.cpp
@@ -80,17 +80,18 @@ oboe::DataCallbackResult OboeStreamCallbackProxy::onAudioReady(
return callbackResult;
}
-void OboeStreamCallbackProxy::onErrorBeforeClose(oboe::AudioStream *audioStream, oboe::Result error) {
- LOGD("OboeStreamCallbackProxy::%s(%p, %d) called", __func__, audioStream, error);
- mErrorCallbackResult = error;
- if (mCallback != nullptr) {
- mCallback->onErrorBeforeClose(audioStream, error);
- }
-}
-
-void OboeStreamCallbackProxy::onErrorAfterClose(oboe::AudioStream *audioStream, oboe::Result error) {
- LOGD("OboeStreamCallbackProxy::%s(%p, %d) called", __func__, audioStream, error);
- if (mCallback != nullptr) {
- mCallback->onErrorAfterClose(audioStream, error);
- }
-}
+// FIXME
+//void OboeStreamCallbackProxy::onErrorBeforeClose(oboe::AudioStream *audioStream, oboe::Result error) {
+// LOGD("OboeStreamCallbackProxy::%s(%p, %d) called", __func__, audioStream, error);
+// mErrorCallbackResult = error;
+// if (mCallback != nullptr) {
+// mCallback->onErrorBeforeClose(audioStream, error);
+// }
+//}
+//
+//void OboeStreamCallbackProxy::onErrorAfterClose(oboe::AudioStream *audioStream, oboe::Result error) {
+// LOGD("OboeStreamCallbackProxy::%s(%p, %d) called", __func__, audioStream, error);
+// if (mCallback != nullptr) {
+// mCallback->onErrorAfterClose(audioStream, error);
+// }
+//}
diff --git a/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.h b/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.h
index e9cd4fe1..9bfeaca1 100644
--- a/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.h
+++ b/apps/OboeTester/app/src/main/cpp/OboeStreamCallbackProxy.h
@@ -22,7 +22,7 @@
#include "oboe/Oboe.h"
-class OboeStreamCallbackProxy : public oboe::AudioStreamCallback {
+class OboeStreamCallbackProxy : public oboe::AudioStreamDataCallback {
public:
void setCallback(oboe::AudioStreamCallback *callback) {
@@ -54,10 +54,6 @@ public:
void *audioData,
int numFrames) override;
- void onErrorBeforeClose(oboe::AudioStream *audioStream, oboe::Result error) override;
-
- void onErrorAfterClose(oboe::AudioStream *audioStream, oboe::Result error) override;
-
/**
* Specify the amount of artificial workload that will waste CPU cycles
* and increase the CPU load.
@@ -77,10 +73,6 @@ public:
static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC);
- oboe::Result getLastErrorCallbackResult() {
- return mErrorCallbackResult;
- }
-
private:
static constexpr int32_t kWorkloadScaler = 500;
double mWorkload = 0.0;
@@ -90,7 +82,6 @@ private:
static bool mCallbackReturnStop;
int64_t mCallbackCount = 0;
std::atomic<int32_t> mFramesPerCallback{0};
- oboe::Result mErrorCallbackResult = oboe::Result::OK;
};
diff --git a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
index 067db811..09c273fd 100644
--- a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
+++ b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
@@ -381,8 +381,12 @@ Java_com_google_sample_oboe_manualtest_OboeAudioStream_getCallbackCount(
JNIEXPORT jint JNICALL
Java_com_google_sample_oboe_manualtest_OboeAudioStream_getLastErrorCallbackResult(
- JNIEnv *env, jobject) {
- return (jint) engine.getCurrentActivity()->getLastErrorCallbackResult();
+ JNIEnv *env, jobject, jint streamIndex) {
+ oboe::AudioStream *oboeStream = engine.getCurrentActivity()->getStream(streamIndex);
+ if (oboeStream != nullptr) {
+ return (jint) oboeStream->getLastErrorCallbackResult();
+ }
+ return 0;
}
JNIEXPORT jdouble JNICALL
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/OboeAudioStream.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/OboeAudioStream.java
index d13a85ca..32462f15 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/OboeAudioStream.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/OboeAudioStream.java
@@ -204,7 +204,10 @@ abstract class OboeAudioStream extends AudioStreamBase {
public native long getCallbackCount(); // TODO Move to another class?
@Override
- public native int getLastErrorCallbackResult(); // TODO Move to another class?
+ public int getLastErrorCallbackResult() {
+ return getLastErrorCallbackResult(streamIndex);
+ }
+ public native int getLastErrorCallbackResult(int streamIndex);
@Override
public long getFramesWritten() {
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java
index aa944041..ce9a5f40 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java
@@ -262,13 +262,14 @@ public class TestDisconnectActivity extends TestAudioActivity implements Runnabl
return ((config.getDirection() == StreamConfiguration.DIRECTION_OUTPUT) ? "OUT" : "IN")
+ ", Perf = " + StreamConfiguration.convertPerformanceModeToText(
config.getPerformanceMode())
- + ", " + StreamConfiguration.convertSharingModeToText(config.getSharingMode());
+ + ", " + StreamConfiguration.convertSharingModeToText(config.getSharingMode())
+ + ", " + config.getSampleRate();
}
private void testConfiguration(boolean isInput,
int perfMode,
int sharingMode,
- int channelCount,
+ int sampleRate,
boolean requestPlugin) throws InterruptedException {
String actualConfigText = "none";
mSkipTest = false;
@@ -295,7 +296,10 @@ public class TestDisconnectActivity extends TestAudioActivity implements Runnabl
requestedConfig.reset();
requestedConfig.setPerformanceMode(perfMode);
requestedConfig.setSharingMode(sharingMode);
- requestedConfig.setChannelCount(channelCount);
+ requestedConfig.setSampleRate(sampleRate);
+ if (sampleRate != 0) {
+ requestedConfig.setRateConversionQuality(StreamConfiguration.RATE_CONVERSION_QUALITY_MEDIUM);
+ }
log("========================== #" + mTestCount);
log("Requested:");
@@ -427,12 +431,17 @@ public class TestDisconnectActivity extends TestAudioActivity implements Runnabl
}
private void testConfiguration(boolean isInput, int performanceMode,
- int sharingMode) throws InterruptedException {
- int channelCount = 2;
+ int sharingMode, int sampleRate) throws InterruptedException {
boolean requestPlugin = true; // plug IN
- testConfiguration(isInput, performanceMode, sharingMode, channelCount, requestPlugin);
+ testConfiguration(isInput, performanceMode, sharingMode, sampleRate, requestPlugin);
requestPlugin = false; // UNplug
- testConfiguration(isInput, performanceMode, sharingMode, channelCount, requestPlugin);
+ testConfiguration(isInput, performanceMode, sharingMode, sampleRate, requestPlugin);
+ }
+
+ private void testConfiguration(boolean isInput, int performanceMode,
+ int sharingMode) throws InterruptedException {
+ final int sampleRate = 0;
+ testConfiguration(isInput, performanceMode, sharingMode, sampleRate);
}
private void testConfiguration(int performanceMode,
@@ -454,6 +463,8 @@ public class TestDisconnectActivity extends TestAudioActivity implements Runnabl
mFailCount = 0;
// Try several different configurations.
try {
+ testConfiguration(false, StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY,
+ StreamConfiguration.SHARING_MODE_EXCLUSIVE, 44100);
testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY,
StreamConfiguration.SHARING_MODE_EXCLUSIVE);
testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY,
diff --git a/include/oboe/AudioStream.h b/include/oboe/AudioStream.h
index a158aafc..c862de6d 100644
--- a/include/oboe/AudioStream.h
+++ b/include/oboe/AudioStream.h
@@ -393,12 +393,25 @@ public:
* Swap old callback for new callback.
* This not atomic.
* This should only be used internally.
- * @param streamCallback
- * @return previous streamCallback
+ * @param dataCallback
+ * @return previous dataCallback
*/
- AudioStreamCallback *swapCallback(AudioStreamCallback *streamCallback) {
- AudioStreamCallback *previousCallback = mStreamCallback;
- mStreamCallback = streamCallback;
+ AudioStreamDataCallback *swapDataCallback(AudioStreamDataCallback *dataCallback) {
+ AudioStreamDataCallback *previousCallback = mDataCallback;
+ mDataCallback = dataCallback;
+ return previousCallback;
+ }
+
+ /*
+ * Swap old callback for new callback.
+ * This not atomic.
+ * This should only be used internally.
+ * @param errorCallback
+ * @return previous errorCallback
+ */
+ AudioStreamErrorCallback *swapErrorCallback(AudioStreamErrorCallback *errorCallback) {
+ AudioStreamErrorCallback *previousCallback = mErrorCallback;
+ mErrorCallback = errorCallback;
return previousCallback;
}
@@ -419,6 +432,13 @@ public:
ResultWithValue<int32_t> waitForAvailableFrames(int32_t numFrames,
int64_t timeoutNanoseconds);
+ /**
+ * @return last result passed from an error callback
+ */
+ virtual oboe::Result getLastErrorCallbackResult() const {
+ return mErrorCallbackResult;
+ }
+
protected:
/**
@@ -515,8 +535,10 @@ protected:
std::mutex mLock; // for synchronizing start/stop/close
+ oboe::Result mErrorCallbackResult = oboe::Result::OK;
private:
+
// Log the scheduler if it changes.
void checkScheduler();
int mPreviousScheduler = -1;
diff --git a/include/oboe/AudioStreamBase.h b/include/oboe/AudioStreamBase.h
index 787ff20e..73c27dfd 100644
--- a/include/oboe/AudioStreamBase.h
+++ b/include/oboe/AudioStreamBase.h
@@ -100,10 +100,35 @@ public:
int32_t getDeviceId() const { return mDeviceId; }
/**
- * @return the callback object for this stream, if set.
+ * For internal use only.
+ * @return the data callback object for this stream, if set.
*/
- AudioStreamCallback* getCallback() const {
- return mStreamCallback;
+ AudioStreamDataCallback *getDataCallback() const {
+ return mDataCallback;
+ }
+
+ /**
+ * For internal use only.
+ * @return the error callback object for this stream, if set.
+ */
+ AudioStreamErrorCallback *getErrorCallback() const {
+ return mErrorCallback;
+ }
+
+ /**
+ * @return true if a data callback was set for this stream
+ */
+ bool isDataCallbackSpecified() const {
+ return mDataCallback != nullptr;
+ }
+
+ /**
+ * Note that if the app does not set an error callback then a
+ * default one may be provided.
+ * @return true if an error callback was set for this stream
+ */
+ bool isErrorCallbackSpecified() const {
+ return mErrorCallback != nullptr;
}
/**
@@ -148,9 +173,15 @@ public:
}
protected:
+ /** The callback which will be fired when new data is ready to be read/written. **/
+ AudioStreamDataCallback *mDataCallback = nullptr;
+
+ /** The callback which will be fired when an error or a disconnect occurs. **/
+ AudioStreamErrorCallback *mErrorCallback = nullptr;
+
+ /** The callback that combines data and error callback. **/
+ // AudioStreamCallback *mStreamCallback = nullptr;
- /** The callback which will be fired when new data is ready to be read/written **/
- AudioStreamCallback *mStreamCallback = nullptr;
/** Number of audio frames which will be requested in each callback */
int32_t mFramesPerCallback = kUnspecified;
/** Stream channel count */
diff --git a/include/oboe/AudioStreamBuilder.h b/include/oboe/AudioStreamBuilder.h
index 80c5da42..d63578c3 100644
--- a/include/oboe/AudioStreamBuilder.h
+++ b/include/oboe/AudioStreamBuilder.h
@@ -306,8 +306,44 @@ public:
}
/**
+ * Specifies an object to handle data related callbacks from the underlying API.
+ *
+ * <strong>Important: See AudioStreamCallback for restrictions on what may be called
+ * from the callback methods.</strong>
+ *
+ * @param dataCallback
+ * @return pointer to the builder so calls can be chained
+ */
+ AudioStreamBuilder *setDataCallback(oboe::AudioStreamDataCallback *dataCallback) {
+ mDataCallback = dataCallback;
+ return this;
+ }
+
+ /**
+ * Specifies an object to handle error related callbacks from the underlying API.
+ * This can occur when a stream is disconnected because a headset is plugged in or unplugged.
+ * It can also occur if the audio service fails or if an exclusive stream is stolen by
+ * another stream.
+ *
+ * <strong>Important: See AudioStreamCallback for restrictions on what may be called
+ * from the callback methods.</strong>
+ *
+ * <strong>When an error callback occurs, the associated stream must be stopped and closed
+ * in a separate thread.</strong>
+ *
+ * @param errorCallback
+ * @return pointer to the builder so calls can be chained
+ */
+ AudioStreamBuilder *setErrorCallback(oboe::AudioStreamErrorCallback *errorCallback) {
+ mErrorCallback = errorCallback;
+ return this;
+ }
+
+ /**
* Specifies an object to handle data or error related callbacks from the underlying API.
*
+ * This is the equivalent of calling both setDataCallback() and setErrorCallback().
+ *
* <strong>Important: See AudioStreamCallback for restrictions on what may be called
* from the callback methods.</strong>
*
@@ -327,7 +363,9 @@ public:
* @return pointer to the builder so calls can be chained
*/
AudioStreamBuilder *setCallback(AudioStreamCallback *streamCallback) {
- mStreamCallback = streamCallback;
+ // Use the same callback object for both, dual inheritance.
+ mDataCallback = streamCallback;
+ mErrorCallback = streamCallback;
return this;
}
diff --git a/include/oboe/AudioStreamCallback.h b/include/oboe/AudioStreamCallback.h
index f427693e..0018623d 100644
--- a/include/oboe/AudioStreamCallback.h
+++ b/include/oboe/AudioStreamCallback.h
@@ -24,15 +24,16 @@ namespace oboe {
class AudioStream;
/**
- * AudioStreamCallback defines a callback interface for:
- *
- * 1) moving data to/from an audio stream using `onAudioReady`
+ * AudioStreamDataCallback defines a callback interface for
+ * moving data to/from an audio stream using `onAudioReady`
* 2) being alerted when a stream has an error using `onError*` methods
*
+ * It is used with AudioStreamBuilder::setDataCallback().
*/
-class AudioStreamCallback {
+
+class AudioStreamDataCallback {
public:
- virtual ~AudioStreamCallback() = default;
+ virtual ~AudioStreamDataCallback() = default;
/**
* A buffer is ready for processing.
@@ -75,21 +76,60 @@ public:
* If you need to move data, eg. MIDI commands, in or out of the callback function then
* we recommend the use of non-blocking techniques such as an atomic FIFO.
*
- * @param oboeStream pointer to the associated stream
+ * @param audioStream pointer to the associated stream
* @param audioData buffer containing input data or a place to put output data
* @param numFrames number of frames to be processed
* @return DataCallbackResult::Continue or DataCallbackResult::Stop
*/
virtual DataCallbackResult onAudioReady(
- AudioStream *oboeStream,
+ AudioStream *audioStream,
void *audioData,
int32_t numFrames) = 0;
+};
+
+/**
+ * AudioStreamDataCallback defines a callback interface for
+ * being alerted when a stream has an error or is disconnected
+ * using `onError*` methods.
+ *
+ * It is used with AudioStreamBuilder::setErrorCallback().
+ */
+class AudioStreamErrorCallback {
+public:
+ virtual ~AudioStreamErrorCallback() = default;
/**
- * This will be called when an error occurs on a stream or when the stream is disconnected.
+ * This will be called first when an error occurs on a stream or when the stream is disconnected.
+ *
+ * It can be used to override and customize the normal error processing.
+ * Use of this method is considered an advanced technique.
+ * It might, for example, be used if an app want to use a high level lock when
+ * closing and reopening a stream.
+ * Or it might be used when an app want to signal a management thread that handles
+ * all of the stream state.
+ *
+ * If this method returns false then
+ * the stream will be stopped, and onErrorBeforeClose() will be called,
+ * then the stream will be closed and onErrorAfterClose() will be closed.
*
- * Note that this will be called on a different thread than the onAudioReady() thread.
- * This thread will be created by Oboe.
+ * If this method returns true then the normal error processing will not occur.
+ * In that case, the app MUST stop() and close() the stream!
+ *
+ * Note that this will be called on a thread created by Oboe.
+ *
+ * @param audioStream pointer to the associated stream
+ * @param error
+ */
+ virtual bool onError(AudioStream* /* audioStream */, Result /* error */) {
+ return false; // false means the stream will be stopped and closed by Oboe
+ // return true; // true means the stream will be stopped and closed by the app
+ }
+
+ /**
+ * This will be called when an error occurs on a stream or when the stream is disconnected
+ * and if onError() returns false.
+ *
+ * Note that this will be called on a thread created by Oboe.
*
* The underlying stream will already be stopped by Oboe but not yet closed.
* So the stream can be queried.
@@ -97,27 +137,45 @@ public:
* Do not close or delete the stream in this method because it will be
* closed after this method returns.
*
- * @param oboeStream pointer to the associated stream
+ * @param audioStream pointer to the associated stream
* @param error
*/
- virtual void onErrorBeforeClose(AudioStream* /* oboeStream */, Result /* error */) {}
+ virtual void onErrorBeforeClose(AudioStream* /* audioStream */, Result /* error */) {}
/**
- * This will be called when an error occurs on a stream or when the stream is disconnected.
+ * This will be called when an error occurs on a stream or when the stream is disconnected
+ * and if onError() returns false.
+ *
* The underlying AAudio or OpenSL ES stream will already be stopped AND closed by Oboe.
* So the underlying stream cannot be referenced.
* But you can still query most parameters.
*
* This callback could be used to reopen a new stream on another device.
- * You can safely delete the old AudioStream in this method.
*
- * @param oboeStream pointer to the associated stream
+ * @param audioStream pointer to the associated stream
* @param error
*/
- virtual void onErrorAfterClose(AudioStream* /* oboeStream */, Result /* error */) {}
+ virtual void onErrorAfterClose(AudioStream* /* audioStream */, Result /* error */) {}
};
+/**
+ * AudioStreamCallback defines a callback interface for:
+ *
+ * 1) moving data to/from an audio stream using `onAudioReady`
+ * 2) being alerted when a stream has an error using `onError*` methods
+ *
+ * It is used with AudioStreamBuilder::setCallback().
+ *
+ * It combines the interfaces defined by AudioStreamDataCallback and AudioStreamErrorCallback.
+ * This was the original callback object. We now recommend using the individual interfaces.
+ */
+class AudioStreamCallback : public AudioStreamDataCallback,
+ public AudioStreamErrorCallback {
+public:
+ virtual ~AudioStreamCallback() = default;
+};
+
} // namespace oboe
#endif //OBOE_STREAM_CALLBACK_H
diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp
index 6d77c509..0b7a2e8c 100644
--- a/src/aaudio/AudioStreamAAudio.cpp
+++ b/src/aaudio/AudioStreamAAudio.cpp
@@ -62,14 +62,16 @@ static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc(
static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream,
Result error) {
LOGD("%s() - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__);
- oboeStream->requestStop();
- if (oboeStream->getCallback() != nullptr) {
- oboeStream->getCallback()->onErrorBeforeClose(oboeStream, error);
- }
- oboeStream->close();
- if (oboeStream->getCallback() != nullptr) {
+ AudioStreamErrorCallback *errorCallback = oboeStream->getErrorCallback();
+ if (errorCallback == nullptr) return; // should be impossible
+ bool handled = errorCallback->onError(oboeStream, error);
+
+ if (!handled) {
+ oboeStream->requestStop();
+ errorCallback->onErrorBeforeClose(oboeStream, error);
+ oboeStream->close();
// Warning, oboeStream may get deleted by this callback.
- oboeStream->getCallback()->onErrorAfterClose(oboeStream, error);
+ errorCallback->onErrorAfterClose(oboeStream, error);
}
LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__);
}
@@ -101,14 +103,18 @@ bool AudioStreamAAudio::isSupported() {
return openResult == 0;
}
-// Static 'C' wrapper for the error callback method.
+// Static method for the error callback.
+// We use a method so we can access protected methods on the stream.
// Launch a thread to handle the error.
// That other thread can safely stop, close and delete the stream.
void AudioStreamAAudio::internalErrorCallback(
AAudioStream *stream,
void *userData,
aaudio_result_t error) {
+ oboe::Result oboeResult = static_cast<Result>(error);
AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData);
+ LOGI("%s() oboeResult = %d", __func__, oboeResult);
+ oboeStream->mErrorCallbackResult = oboeResult;
// Prevents deletion of the stream if the app is using AudioStreamBuilder::openStream(shared_ptr)
std::shared_ptr<AudioStream> sharedStream = oboeStream->lockWeakThis();
@@ -118,16 +124,14 @@ void AudioStreamAAudio::internalErrorCallback(
if (oboeStream->wasErrorCallbackCalled()) { // block extra error callbacks
LOGE("%s() multiple error callbacks called!", __func__);
} else if (stream != oboeStream->getUnderlyingStream()) {
- LOGW("%s() stream already closed or closing", __func__); // can happen if there are bugs
+ LOGW("%s() stream already closed or closing", __func__); // might happen if there are bugs
} else if (sharedStream) {
// Handle error on a separate thread using shared pointer.
- std::thread t(oboe_aaudio_error_thread_proc_shared, sharedStream,
- static_cast<Result>(error));
+ std::thread t(oboe_aaudio_error_thread_proc_shared, sharedStream, oboeResult);
t.detach();
} else {
// Handle error on a separate thread.
- std::thread t(oboe_aaudio_error_thread_proc, oboeStream,
- static_cast<Result>(error));
+ std::thread t(oboe_aaudio_error_thread_proc, oboeStream, oboeResult);
t.detach();
}
}
@@ -229,13 +233,19 @@ Result AudioStreamAAudio::open() {
// TODO get more parameters from the builder?
- if (mStreamCallback != nullptr) {
+ if (isDataCallbackSpecified()) {
mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerCallback());
- // If the data callback is not being used then the write method will return an error
- // and the app can stop and close the stream.
+
+ if (!isErrorCallbackSpecified()) {
+ // The app did not specify a callback so we should specify
+ // our own so the stream gets closed and stopped.
+ mErrorCallback = &mDefaultErrorCallback;
+ }
mLibLoader->builder_setErrorCallback(aaudioBuilder, internalErrorCallback, this);
}
+ // Else if the data callback is not being used then the write method will return an error
+ // and the app can stop and close the stream.
// ============= OPEN THE STREAM ================
{
@@ -357,7 +367,7 @@ Result AudioStreamAAudio::requestStart() {
return Result::OK;
}
}
- if (mStreamCallback != nullptr) { // Was a callback requested?
+ if (isDataCallbackSpecified()) {
setDataCallbackEnabled(true);
}
return static_cast<Result>(mLibLoader->stream_requestStart(stream));
diff --git a/src/aaudio/AudioStreamAAudio.h b/src/aaudio/AudioStreamAAudio.h
index 90958170..83f4b2fe 100644
--- a/src/aaudio/AudioStreamAAudio.h
+++ b/src/aaudio/AudioStreamAAudio.h
@@ -118,10 +118,13 @@ private:
std::atomic<bool> mCallbackThreadEnabled;
- // pointer to the underlying AAudio stream, valid if open, null if closed
+ // pointer to the underlying 'C' AAudio stream, valid if open, null if closed
std::atomic<AAudioStream *> mAAudioStream{nullptr};
static AAudioLoader *mLibLoader;
+
+ // We may not use this but it is so small that it is not worth allocating dynamically.
+ AudioStreamErrorCallback mDefaultErrorCallback;
};
} // namespace oboe
diff --git a/src/common/AudioSourceCaller.cpp b/src/common/AudioSourceCaller.cpp
index 0180c226..854ade9c 100644
--- a/src/common/AudioSourceCaller.cpp
+++ b/src/common/AudioSourceCaller.cpp
@@ -20,7 +20,7 @@ using namespace oboe;
using namespace flowgraph;
int32_t AudioSourceCaller::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
- oboe::AudioStreamCallback *callback = mStream->getCallback();
+ oboe::AudioStreamDataCallback *callback = mStream->getDataCallback();
int32_t result = 0;
int32_t numFrames = numBytes / mStream->getBytesPerFrame();
if (callback != nullptr) {
diff --git a/src/common/AudioStream.cpp b/src/common/AudioStream.cpp
index 7bcd087c..8a9cd452 100644
--- a/src/common/AudioStream.cpp
+++ b/src/common/AudioStream.cpp
@@ -59,10 +59,10 @@ DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFra
}
DataCallbackResult result;
- if (mStreamCallback == nullptr) {
- result = onDefaultCallback(audioData, numFrames);
+ if (mDataCallback) {
+ result = mDataCallback->onAudioReady(this, audioData, numFrames);
} else {
- result = mStreamCallback->onAudioReady(this, audioData, numFrames);
+ result = onDefaultCallback(audioData, numFrames);
}
// On Oreo, we might get called after returning stop.
// So block that here.
diff --git a/src/common/DataConversionFlowGraph.cpp b/src/common/DataConversionFlowGraph.cpp
index 7cc742ee..ef99d6a7 100644
--- a/src/common/DataConversionFlowGraph.cpp
+++ b/src/common/DataConversionFlowGraph.cpp
@@ -101,8 +101,9 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
// Source
// IF OUTPUT and using a callback then call back to the app using a SourceCaller.
// OR IF INPUT and NOT using a callback then read from the child stream using a SourceCaller.
- if ((sourceStream->getCallback() != nullptr && isOutput)
- || (sourceStream->getCallback() == nullptr && isInput)) {
+ bool isDataCallbackSpecified = sourceStream->isDataCallbackSpecified();
+ if ((isDataCallbackSpecified && isOutput)
+ || (!isDataCallbackSpecified && isInput)) {
int32_t actualSourceFramesPerCallback = (sourceFramesPerCallback == kUnspecified)
? sourceStream->getFramesPerBurst()
: sourceFramesPerCallback;
@@ -236,7 +237,7 @@ int32_t DataConversionFlowGraph::write(void *inputBuffer, int32_t numFrames) {
int32_t DataConversionFlowGraph::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
int32_t numFrames = numBytes / mFilterStream->getBytesPerFrame();
- mCallbackResult = mFilterStream->getCallback()->onAudioReady(mFilterStream, buffer, numFrames);
+ mCallbackResult = mFilterStream->getDataCallback()->onAudioReady(mFilterStream, buffer, numFrames);
// TODO handle STOP from callback, process data remaining in the block adapter
return numBytes;
-} \ No newline at end of file
+}
diff --git a/src/common/FilterAudioStream.cpp b/src/common/FilterAudioStream.cpp
index a4715835..bacb54b4 100644
--- a/src/common/FilterAudioStream.cpp
+++ b/src/common/FilterAudioStream.cpp
@@ -16,6 +16,7 @@
#include <memory>
+#include "OboeDebug.h"
#include "FilterAudioStream.h"
using namespace oboe;
@@ -90,3 +91,17 @@ ResultWithValue<int32_t> FilterAudioStream::read(void *buffer,
return ResultWithValue<int32_t>::createBasedOnSign(framesRead);
}
+DataCallbackResult FilterAudioStream::onAudioReady(AudioStream *oboeStream,
+ void *audioData,
+ int32_t numFrames) {
+ int32_t framesProcessed;
+ if (oboeStream->getDirection() == Direction::Output) {
+ framesProcessed = mFlowGraph->read(audioData, numFrames, 0 /* timeout */);
+ } else {
+ framesProcessed = mFlowGraph->write(audioData, numFrames);
+ }
+ LOGI("%s() framesProcessed = %d, numFrames = %d", __func__, framesProcessed, numFrames);
+ return (framesProcessed < numFrames)
+ ? DataCallbackResult::Stop
+ : mFlowGraph->getDataCallbackResult();
+} \ No newline at end of file
diff --git a/src/common/FilterAudioStream.h b/src/common/FilterAudioStream.h
index 3949de77..be652c9d 100644
--- a/src/common/FilterAudioStream.h
+++ b/src/common/FilterAudioStream.h
@@ -42,8 +42,11 @@ public:
: AudioStream(builder)
, mChildStream(childStream) {
// Intercept the callback if used.
- if (builder.getCallback() != nullptr) {
- mStreamCallback = mChildStream->swapCallback(this);
+ if (builder.getErrorCallback() != nullptr) {
+ mErrorCallback = mChildStream->swapErrorCallback(this);
+ }
+ if (builder.getDataCallback() != nullptr) {
+ mDataCallback = mChildStream->swapDataCallback(this);
} else {
const int size = childStream->getFramesPerBurst() * childStream->getBytesPerFrame();
mBlockingBuffer = std::make_unique<uint8_t[]>(size);
@@ -175,32 +178,36 @@ public:
DataCallbackResult onAudioReady(AudioStream *oboeStream,
void *audioData,
- int32_t numFrames) override {
- int32_t framesProcessed;
- if (oboeStream->getDirection() == Direction::Output) {
- framesProcessed = mFlowGraph->read(audioData, numFrames, 0 /* timeout */);
- } else {
- framesProcessed = mFlowGraph->write(audioData, numFrames);
+ int32_t numFrames) override;
+
+ bool onError(AudioStream * audioStream, Result error) override {
+ if (mErrorCallback != nullptr) {
+ return mErrorCallback->onError(this, error);
}
- return (framesProcessed < numFrames)
- ? DataCallbackResult::Stop
- : mFlowGraph->getDataCallbackResult();
+ return false;
}
void onErrorBeforeClose(AudioStream *oboeStream, Result error) override {
- if (mStreamCallback != nullptr) {
- mStreamCallback->onErrorBeforeClose(this, error);
+ if (mErrorCallback != nullptr) {
+ mErrorCallback->onErrorBeforeClose(this, error);
}
}
void onErrorAfterClose(AudioStream *oboeStream, Result error) override {
// Close this parent stream because the callback will only close the child.
AudioStream::close();
- if (mStreamCallback != nullptr) {
- mStreamCallback->onErrorAfterClose(this, error);
+ if (mErrorCallback != nullptr) {
+ mErrorCallback->onErrorAfterClose(this, error);
}
}
+ /**
+ * @return last result passed from an error callback
+ */
+ oboe::Result getLastErrorCallbackResult() const override {
+ return mChildStream->getLastErrorCallbackResult();
+ }
+
private:
std::unique_ptr<AudioStream> mChildStream; // this stream wraps the child stream
diff --git a/src/common/QuirksManager.cpp b/src/common/QuirksManager.cpp
index 21a52048..68f328e0 100644
--- a/src/common/QuirksManager.cpp
+++ b/src/common/QuirksManager.cpp
@@ -138,7 +138,7 @@ bool QuirksManager::isConversionNeeded(
// know if we will get an MMAP stream. So, to be safe, just do the conversion in Oboe.
if (OboeGlobals::areWorkaroundsEnabled()
&& builder.willUseAAudio()
- && builder.getCallback() != nullptr
+ && builder.isDataCallbackSpecified()
&& builder.getFramesPerCallback() != 0
&& getSdkVersion() <= __ANDROID_API_R__) {
LOGI("QuirksManager::%s() avoid setFramesPerCallback(n>0)", __func__);
diff --git a/src/opensles/AudioStreamBuffered.cpp b/src/opensles/AudioStreamBuffered.cpp
index 85b9f8e4..8ee01667 100644
--- a/src/opensles/AudioStreamBuffered.cpp
+++ b/src/opensles/AudioStreamBuffered.cpp
@@ -260,7 +260,7 @@ int32_t AudioStreamBuffered::getBufferCapacityInFrames() const {
bool AudioStreamBuffered::isXRunCountSupported() const {
// XRun count is only supported if we're using blocking I/O (not callbacks)
- return (getCallback() == nullptr);
+ return (!isDataCallbackSpecified());
}
} // namespace oboe \ No newline at end of file
diff --git a/src/opensles/AudioStreamBuffered.h b/src/opensles/AudioStreamBuffered.h
index 5923e8db..2b6152b6 100644
--- a/src/opensles/AudioStreamBuffered.h
+++ b/src/opensles/AudioStreamBuffered.h
@@ -60,7 +60,7 @@ protected:
DataCallbackResult onDefaultCallback(void *audioData, int numFrames) override;
// If there is no callback then we need a FIFO between the App and OpenSL ES.
- bool usingFIFO() const { return getCallback() == nullptr; }
+ bool usingFIFO() const { return !isDataCallbackSpecified(); }
virtual Result updateServiceFrameCounter() = 0;
diff --git a/tests/testStreamClosedMethods.cpp b/tests/testStreamClosedMethods.cpp
index a1f1105a..cbb2191c 100644
--- a/tests/testStreamClosedMethods.cpp
+++ b/tests/testStreamClosedMethods.cpp
@@ -19,15 +19,13 @@
using namespace oboe;
-class MyCallback : public AudioStreamCallback {
+class MyCallback : public AudioStreamDataCallback {
public:
DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
return DataCallbackResult::Continue;
}
};
-
-
class StreamClosedReturnValues : public ::testing::Test {
protected:
@@ -134,13 +132,13 @@ TEST_F(StreamClosedReturnValues, GetDeviceIdReturnsLastKnownValue) {
ASSERT_EQ(mStream->getDeviceId(), d);
}
-TEST_F(StreamClosedReturnValues, GetCallbackReturnsLastKnownValue) {
+TEST_F(StreamClosedReturnValues, GetDataCallbackReturnsLastKnownValue) {
- AudioStreamCallback *callback = new MyCallback();
- mBuilder.setCallback(callback);
+ AudioStreamDataCallback *callback = new MyCallback();
+ mBuilder.setDataCallback(callback);
ASSERT_TRUE(openAndCloseStream());
- AudioStreamCallback *callback2 = mStream->getCallback();
+ AudioStreamDataCallback *callback2 = mStream->getDataCallback();
ASSERT_EQ(callback, callback2);
}