OboeTester: add checkbox for MMAP mode
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
index 4538f26..2f2e7e5 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
@@ -100,26 +100,9 @@
bool ActivityContext::isMMapUsed(int32_t streamIndex) {
oboe::AudioStream *oboeStream = getStream(streamIndex);
- if (oboeStream != nullptr && oboeStream->usesAAudio()) {
- if (mAAudioStream_isMMap == nullptr) {
- mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
- if (mLibHandle == nullptr) {
- LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__);
- return false;
- }
-
- mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
- dlsym(mLibHandle, FUNCTION_IS_MMAP);
-
- if(mAAudioStream_isMMap == nullptr) {
- LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__);
- return false;
- }
- }
- AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream();
- return mAAudioStream_isMMap(aaudioStream);
- }
- return false;
+ if (oboeStream == nullptr) return false;
+ if (oboeStream->getAudioApi() != AudioApi::AAudio) return false;
+ return AAudioExtensions::getInstance().isMMapUsed(oboeStream);
}
oboe::Result ActivityContext::pause() {
@@ -159,20 +142,20 @@
}
}
-int ActivityContext::open(
- jint nativeApi,
- jint sampleRate,
- jint channelCount,
- jint format,
- jint sharingMode,
- jint performanceMode,
- jint deviceId,
- jint sessionId,
- jint framesPerBurst,
- jboolean channelConversionAllowed,
- jboolean formatConversionAllowed,
- jint rateConversionQuality,
- jboolean isInput) {
+int ActivityContext::open(jint nativeApi,
+ jint sampleRate,
+ jint channelCount,
+ jint format,
+ jint sharingMode,
+ jint performanceMode,
+ jint deviceId,
+ jint sessionId,
+ jint framesPerBurst,
+ jboolean channelConversionAllowed,
+ jboolean formatConversionAllowed,
+ jint rateConversionQuality,
+ jboolean isMMap,
+ jboolean isInput) {
oboe::AudioApi audioApi = oboe::AudioApi::Unspecified;
switch (nativeApi) {
@@ -218,10 +201,15 @@
}
builder.setAudioApi(audioApi);
+ // Temporarily set the AAudio MMAP policy to disable MMAP or not.
+ bool oldMMapEnabled = AAudioExtensions::getInstance().isMMapEnabled();
+ AAudioExtensions::getInstance().setMMapEnabled(isMMap);
+
// Open a stream based on the builder settings.
oboe::AudioStream *oboeStream = nullptr;
oboe::Result result = builder.openStream(&oboeStream);
LOGD("ActivityContext::open() builder.openStream() returned %d", result);
+ AAudioExtensions::getInstance().setMMapEnabled(oldMMapEnabled);
if (result != oboe::Result::OK) {
delete oboeStream;
oboeStream = nullptr;
@@ -239,6 +227,7 @@
finishOpen(isInput, oboeStream);
}
+
if (!mUseCallback) {
int numSamples = getFramesPerBlock() * mChannelCount;
dataBuffer = std::make_unique<float[]>(numSamples);
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
index 6022c81..7d6aa8b 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
@@ -66,12 +66,105 @@
#define LIB_AAUDIO_NAME "libaaudio.so"
#define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed"
+#define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy"
+#define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy"
#define SECONDS_TO_RECORD 10
typedef struct AAudioStreamStruct AAudioStream;
/**
+ * Call some AAudio test routines that are not part of the normal API.
+ */
+class AAudioExtensions {
+public:
+ static AAudioExtensions &getInstance() {
+ static AAudioExtensions instance;
+ return instance;
+ }
+
+ bool isMMapUsed(oboe::AudioStream *oboeStream) {
+ if (!loadLibrary()) return false;
+ AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream();
+ return mAAudioStream_isMMap(aaudioStream);
+ }
+
+ bool setMMapEnabled(bool enabled) {
+ if (!loadLibrary()) return false;
+ return mAAudio_setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER);
+ }
+
+ bool isMMapEnabled() {
+ if (!loadLibrary()) return false;
+ return mAAudio_getMMapPolicy() != AAUDIO_POLICY_NEVER;
+ }
+
+ bool isMMapSupported() {
+ if (!loadLibrary()) return false;
+ return mMMapSupported;
+ }
+
+private:
+
+ enum {
+ AAUDIO_POLICY_NEVER = 1,
+ AAUDIO_POLICY_AUTO,
+ AAUDIO_POLICY_ALWAYS
+ };
+ typedef int32_t aaudio_policy_t;
+
+ // return true if it succeeds
+ bool loadLibrary() {
+ if (mFirstTime) {
+ mFirstTime = false;
+ mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
+ if (mLibHandle == nullptr) {
+ LOGI("%s() could not find "
+ LIB_AAUDIO_NAME, __func__);
+ return false;
+ }
+
+ mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
+ dlsym(mLibHandle, FUNCTION_IS_MMAP);
+
+ if (mAAudioStream_isMMap == nullptr) {
+ LOGI("%s() could not find "
+ FUNCTION_IS_MMAP, __func__);
+ return false;
+ }
+
+ mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy))
+ dlsym(mLibHandle, FUNCTION_SET_MMAP_POLICY);
+
+ if (mAAudio_setMMapPolicy == nullptr) {
+ LOGI("%s() could not find "
+ FUNCTION_SET_MMAP_POLICY, __func__);
+ return false;
+ }
+
+ mAAudio_getMMapPolicy = (aaudio_policy_t (*)())
+ dlsym(mLibHandle, FUNCTION_GET_MMAP_POLICY);
+
+ if (mAAudio_getMMapPolicy == nullptr) {
+ LOGI("%s() could not find "
+ FUNCTION_GET_MMAP_POLICY, __func__);
+ return false;
+ }
+
+ mMMapSupported = isMMapEnabled(); // still supported even if disabled later
+ }
+ return true;
+ }
+
+ bool mFirstTime = true;
+ void *mLibHandle = nullptr;
+ bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr;
+ int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
+ aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr;
+ bool mMMapSupported = false;
+};
+
+/**
* Abstract base class that corresponds to a test at the Java level.
*/
class ActivityContext {
@@ -103,6 +196,7 @@
jboolean channelConversionAllowed,
jboolean formatConversionAllowed,
jint rateConversionQuality,
+ jboolean isMMap,
jboolean isInput);
@@ -213,9 +307,6 @@
std::atomic<bool> threadEnabled{false};
std::thread *dataThread = nullptr;
- bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr;
- void *mLibHandle = nullptr;
-
private:
};
diff --git a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
index 24de205..2a5b13f 100644
--- a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
+++ b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
@@ -48,6 +48,7 @@
jboolean channelConversionAllowed,
jboolean formatConversionAllowed,
jint rateConversionQuality,
+ jboolean isMMap,
jboolean isInput);
JNIEXPORT void JNICALL
Java_com_google_sample_oboe_manualtest_OboeAudioStream_close(JNIEnv *env, jobject, jint);
@@ -85,6 +86,11 @@
/********************** JNI Implementations *************************************/
/*********************************************************************************/
+JNIEXPORT jboolean JNICALL
+Java_com_google_sample_oboe_manualtest_NativeEngine_isMMapSupported(JNIEnv *env, jclass type) {
+ return AAudioExtensions::getInstance().isMMapSupported();
+}
+
JNIEXPORT jint JNICALL
Java_com_google_sample_oboe_manualtest_OboeAudioStream_openNative(
JNIEnv *env, jobject synth,
@@ -100,22 +106,24 @@
jboolean channelConversionAllowed,
jboolean formatConversionAllowed,
jint rateConversionQuality,
+ jboolean isMMap,
jboolean isInput) {
LOGD("OboeAudioStream_openNative: sampleRate = %d, framesPerBurst = %d", sampleRate, framesPerBurst);
return (jint) engine.getCurrentActivity()->open(nativeApi,
- sampleRate,
- channelCount,
- format,
- sharingMode,
- performanceMode,
- deviceId,
- sessionId,
- framesPerBurst,
- channelConversionAllowed,
- formatConversionAllowed,
- rateConversionQuality,
- isInput);
+ sampleRate,
+ channelCount,
+ format,
+ sharingMode,
+ performanceMode,
+ deviceId,
+ sessionId,
+ framesPerBurst,
+ channelConversionAllowed,
+ formatConversionAllowed,
+ rateConversionQuality,
+ isMMap,
+ isInput);
}
JNIEXPORT jint JNICALL
@@ -541,4 +549,4 @@
return engine.mActivityGlitches.getGlitchAnalyzer()->getPeakAmplitude();
}
-}
\ No newline at end of file
+}
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/NativeEngine.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/NativeEngine.java
new file mode 100644
index 0000000..592003d
--- /dev/null
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/NativeEngine.java
@@ -0,0 +1,6 @@
+package com.google.sample.oboe.manualtest;
+
+public class NativeEngine {
+
+ static native boolean isMMapSupported();
+}
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 b9a96a7..a6110cf 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
@@ -68,6 +68,7 @@
requestedConfiguration.getChannelConversionAllowed(),
requestedConfiguration.getFormatConversionAllowed(),
requestedConfiguration.getRateConversionQuality(),
+ requestedConfiguration.isMMap(),
isInput()
);
if (result < 0) {
@@ -105,6 +106,7 @@
boolean channelConversionAllowed,
boolean formatConversionAllowed,
int rateConversionQuality,
+ boolean isMMap,
boolean isInput);
@Override
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java
index 3b372cb..b19623a 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java
@@ -69,6 +69,7 @@
private int mRateConversionQuality;
private int mFramesPerBurst = 0;
+
private boolean mMMap = false;
public StreamConfiguration() {
@@ -93,6 +94,7 @@
mChannelConversionAllowed = false;
mFormatConversionAllowed = false;
mRateConversionQuality = RATE_CONVERSION_QUALITY_NONE;
+ mMMap = NativeEngine.isMMapSupported();
}
public int getFramesPerBurst() {
@@ -273,4 +275,5 @@
public int getRateConversionQuality() {
return mRateConversionQuality;
}
+
}
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfigurationView.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfigurationView.java
index 9cd54b0..2f7fe99 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfigurationView.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfigurationView.java
@@ -44,6 +44,8 @@
protected Spinner mNativeApiSpinner;
private TextView mActualNativeApiView;
+ private TextView mActualMMapView;
+ private CheckBox mRequestedMMapView;
private TextView mActualExclusiveView;
private TextView mActualPerformanceView;
private Spinner mPerformanceSpinner;
@@ -80,6 +82,10 @@
}
};
+ public static String yesOrNo(boolean b) {
+ return b ? "YES" : "NO";
+ }
+
private void updateSettingsViewText() {
if (mHideableView.isShown()) {
mOptionExpander.setText(mHideSettingsText);
@@ -162,6 +168,18 @@
}
});
+ mActualMMapView = (TextView) findViewById(R.id.actualMMap);
+ mRequestedMMapView = (CheckBox) findViewById(R.id.requestedMMapEnable);
+ mRequestedMMapView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mRequestedConfiguration.setMMap(mRequestedMMapView.isChecked());
+ }
+ });
+ boolean mmapSupported = NativeEngine.isMMapSupported();
+ mRequestedMMapView.setEnabled(mmapSupported);
+ mRequestedMMapView.setChecked(mmapSupported);
+
mActualExclusiveView = (TextView) findViewById(R.id.actualExclusiveMode);
mRequestedExclusiveView = (CheckBox) findViewById(R.id.requestedExclusiveMode);
mRequestedExclusiveView.setOnClickListener(new View.OnClickListener() {
@@ -337,8 +355,10 @@
value = mActualConfiguration.getNativeApi();
mActualNativeApiView.setText(StreamConfiguration.convertNativeApiToText(value));
- value = mActualConfiguration.getSharingMode();
- mActualExclusiveView.setText(StreamConfiguration.convertSharingModeToText(value));
+ mActualMMapView.setText(yesOrNo(mActualConfiguration.isMMap()));
+ int sharingMode = mActualConfiguration.getSharingMode();
+ boolean isExclusive = (sharingMode == StreamConfiguration.SHARING_MODE_EXCLUSIVE);
+ mActualExclusiveView.setText(yesOrNo(isExclusive));
value = mActualConfiguration.getPerformanceMode();
mActualPerformanceView.setText(StreamConfiguration.convertPerformanceModeToText(value));
@@ -352,10 +372,12 @@
mActualSampleRateView.setText(mActualConfiguration.getSampleRate() + "");
mActualSessionIdView.setText("S#: " + mActualConfiguration.getSessionId());
+ boolean isMMap = mActualConfiguration.isMMap();
mStreamInfoView.setText("burst = " + mActualConfiguration.getFramesPerBurst()
+ ", capacity = " + mActualConfiguration.getBufferCapacityInFrames()
+ ", devID = " + mActualConfiguration.getDeviceId()
+ ", " + (mActualConfiguration.isMMap() ? "MMAP" : "Legacy")
+ + (isMMap ? ", " + StreamConfiguration.convertSharingModeToText(sharingMode) : "")
);
mHideableView.requestLayout();
diff --git a/apps/OboeTester/app/src/main/res/layout/activity_auto_glitches.xml b/apps/OboeTester/app/src/main/res/layout/activity_auto_glitches.xml
index e16e3af..c727096 100644
--- a/apps/OboeTester/app/src/main/res/layout/activity_auto_glitches.xml
+++ b/apps/OboeTester/app/src/main/res/layout/activity_auto_glitches.xml
@@ -68,7 +68,7 @@
android:id="@+id/text_analyzer_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:lines="8"
+ android:lines="12"
android:text="@string/auto_glitch_instructions"
android:textSize="18sp"
android:textStyle="bold"
diff --git a/apps/OboeTester/app/src/main/res/layout/stream_config.xml b/apps/OboeTester/app/src/main/res/layout/stream_config.xml
index 27d2153..70435c2 100644
--- a/apps/OboeTester/app/src/main/res/layout/stream_config.xml
+++ b/apps/OboeTester/app/src/main/res/layout/stream_config.xml
@@ -44,6 +44,22 @@
android:text="\?" />
</TableRow>
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Device: " />
+
+ <com.google.sample.audio_device.AudioDeviceSpinner
+ android:id="@+id/devices_spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </TableRow>
+
<TableRow>
<TextView
@@ -131,22 +147,62 @@
android:text="\?" />
</TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ </TableLayout>
- <TextView
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/requestedMMapEnable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="Device: " />
+ android:layout_marginRight="8sp"
+ android:text="MMAP" />
- <com.google.sample.audio_device.AudioDeviceSpinner
- android:id="@+id/devices_spinner"
+ <TextView
+ android:id="@+id/actualMMap"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:layout_marginRight="12sp"
+ android:text="\?" />
- </TableRow>
- </TableLayout>
+ <CheckBox
+ android:id="@+id/requestedExclusiveMode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="8sp"
+ android:text="Exclusive" />
+
+ <TextView
+ android:id="@+id/actualExclusiveMode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="15sp"
+ android:text="\?" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/requestAudioEffect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="10sp"
+ android:text="Effect" />
+
+ <TextView
+ android:id="@+id/sessionId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="\?" />
+
+ </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
@@ -181,44 +237,6 @@
</LinearLayout>
- <TableLayout
- android:id="@+id/flagTable"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:shrinkColumns="*"
- >
- <TableRow>
-
- <CheckBox
- android:id="@+id/requestedExclusiveMode"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="30sp"
- android:text="Exclusive" />
-
- <TextView
- android:id="@+id/actualExclusiveMode"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="\?" />
-
-
- <CheckBox
- android:id="@+id/requestAudioEffect"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="30sp"
- android:text="Effect" />
-
- <TextView
- android:id="@+id/sessionId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="\?" />
-
- </TableRow>
-
- </TableLayout>
</LinearLayout>