Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
47
externals/oboe/tests/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
# Usually this file is called from run_tests.sh which requires the $ANDROID_NDK variable to be set so there's no need
|
||||
# to set it here. Comments below are left in intentionally in case they're useful in future.
|
||||
# This may work on Mac OS.
|
||||
# set(ANDROID_NDK $ENV{HOME}/Library/Android/sdk/ndk-bundle)
|
||||
# This may work on Linux.
|
||||
# set(ANDROID_NDK $ENV{HOME}/Android/sdk/ndk-bundle)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -std=c++17 -DOBOE_SUPPRESS_LOG_SPAM")
|
||||
|
||||
# Include GoogleTest library
|
||||
set(GOOGLETEST_ROOT ${ANDROID_NDK}/sources/third_party/googletest)
|
||||
add_library(gtest STATIC ${GOOGLETEST_ROOT}/src/gtest_main.cc ${GOOGLETEST_ROOT}/src/gtest-all.cc)
|
||||
target_include_directories(gtest PRIVATE ${GOOGLETEST_ROOT})
|
||||
target_include_directories(gtest PUBLIC ${GOOGLETEST_ROOT}/include)
|
||||
|
||||
# Include Oboe sources
|
||||
set (OBOE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
add_subdirectory(${OBOE_DIR} ./oboe-bin)
|
||||
include_directories(
|
||||
${OBOE_DIR}/include
|
||||
${OBOE_DIR}/src
|
||||
)
|
||||
|
||||
# Build the test binary
|
||||
add_executable(
|
||||
testOboe
|
||||
testAAudio.cpp
|
||||
testAudioClock.cpp
|
||||
testFlowgraph.cpp
|
||||
testFullDuplexStream.cpp
|
||||
testResampler.cpp
|
||||
testReturnStop.cpp
|
||||
testReturnStopDeadlock.cpp
|
||||
testStreamClosedMethods.cpp
|
||||
testStreamFramesProcessed.cpp
|
||||
testStreamOpen.cpp
|
||||
testStreamStates.cpp
|
||||
testStreamStop.cpp
|
||||
testStreamWaitState.cpp
|
||||
testXRunBehaviour.cpp
|
||||
testUtilities.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(testOboe gtest oboe)
|
||||
target_link_options(testOboe PRIVATE "-Wl,-z,max-page-size=16384")
|
70
externals/oboe/tests/README.md
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
# Oboe Unit Tests
|
||||
|
||||
This directory contains the Oboe unit tests. They are run using the bash script `run_tests.sh`.
|
||||
|
||||
The basic operation is:
|
||||
|
||||
1. Connect an Android device or start the Android emulator
|
||||
2. Open a terminal window and execute `run_tests.sh`
|
||||
|
||||
## Prerequisites/caveats
|
||||
|
||||
1. Java JDK installed.
|
||||
2. On Mac, you must agree to the XCode license. The script will prompt you.
|
||||
3. You must have compiled and executed one of the Oboe examples or OboeTester. That ensures that the NDK and cmake is installed.
|
||||
4. You must define `ANDROID_NDK` as an environment variable and make sure `cmake` is on your path.
|
||||
|
||||
To test this on Mac or Linux enter:
|
||||
|
||||
echo $ANDROID_HOME
|
||||
echo $ANDROID_NDK
|
||||
cmake --version
|
||||
|
||||
They may already be set. If so then skip to "Running the Tests" below.
|
||||
|
||||
If not, then this may work on Mac OS:
|
||||
|
||||
export ANDROID_HOME=$HOME/Library/Android/sdk
|
||||
|
||||
or this may work on Linux:
|
||||
|
||||
export ANDROID_HOME=$HOME/Android/Sdk
|
||||
|
||||
Tadb rooto determine the latest installed version of the NDK. Enter:
|
||||
|
||||
ls $ANDROID_HOME/ndk
|
||||
|
||||
Make note of the folder name. Mine was "21.3.6528147" so I entered:
|
||||
|
||||
export ANDROID_NDK=$ANDROID_HOME/ndk/21.3.6528147/
|
||||
|
||||
If you need to add `cmake` to your path then you can find it by entering:
|
||||
|
||||
ls $ANDROID_HOME/cmake
|
||||
|
||||
Make note of the folder name. Mine was "3.10.2.4988404" so I entered:
|
||||
|
||||
export PATH=$PATH:$ANDROID_HOME/cmake/3.10.2.4988404/bin
|
||||
cmake --version
|
||||
|
||||
## Running the Tests
|
||||
|
||||
To run the tests, enter:
|
||||
|
||||
cd tests
|
||||
./run_tests.sh
|
||||
|
||||
When the tests finish, you may need to enter \<control-c\> to exit the script.
|
||||
|
||||
If you get this error:
|
||||
|
||||
com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException:
|
||||
INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package com.google.oboe.tests.unittestrunner
|
||||
signatures do not match previously installed version; ignoring!
|
||||
|
||||
then uninstall the app "UnitTestRunner" from the Android device. Or try:
|
||||
|
||||
adb root
|
||||
adb remount -R
|
||||
|
||||
See `run_tests.sh` for more documentation
|
11
externals/oboe/tests/UnitTestRunner/.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches/build_file_checksums.ser
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
1
externals/oboe/tests/UnitTestRunner/app/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
30
externals/oboe/tests/UnitTestRunner/app/build.gradle
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 34
|
||||
defaultConfig {
|
||||
applicationId "com.google.oboe.tests.unittestrunner"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path file('../../CMakeLists.txt')
|
||||
}
|
||||
}
|
||||
namespace 'com.google.oboe.tests.unittestrunner'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
}
|
21
externals/oboe/tests/UnitTestRunner/app/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
2
externals/oboe/tests/UnitTestRunner/app/src/main/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/assets
|
||||
/jniLibs
|
21
externals/oboe/tests/UnitTestRunner/app/src/main/AndroidManifest.xml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".MainActivity" android:exported="true" android:screenOrientation="landscape">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,198 @@
|
|||
package com.google.oboe.tests.unittestrunner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private final String TAG = MainActivity.class.getName();
|
||||
private static final String TEST_BINARY_FILENAME = "testOboe.so";
|
||||
private static final int APP_PERMISSION_REQUEST = 0;
|
||||
|
||||
private TextView outputText;
|
||||
private ScrollView scrollView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
outputText = findViewById(R.id.output_view_text);
|
||||
scrollView = findViewById(R.id.scroll_view);
|
||||
runCommand();
|
||||
}
|
||||
|
||||
private void runCommand(){
|
||||
if (!isRecordPermissionGranted()){
|
||||
requestPermissions();
|
||||
} else {
|
||||
Log.d(TAG, "Got RECORD_AUDIO permission");
|
||||
Thread commandThread = new Thread(new UnitTestCommand());
|
||||
commandThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
private String executeBinary() {
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
try {
|
||||
String executablePath;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
executablePath = getApplicationInfo().nativeLibraryDir + "/" + TEST_BINARY_FILENAME;
|
||||
} else {
|
||||
executablePath = getExecutablePathFromAssets();
|
||||
}
|
||||
|
||||
Log.d(TAG, "Attempting to execute " + executablePath);
|
||||
|
||||
Process process = Runtime.getRuntime().exec(executablePath);
|
||||
|
||||
BufferedReader stdInput = new BufferedReader(new
|
||||
InputStreamReader(process.getInputStream()));
|
||||
|
||||
BufferedReader stdError = new BufferedReader(new
|
||||
InputStreamReader(process.getErrorStream()));
|
||||
|
||||
// read the output from the command
|
||||
String s;
|
||||
while ((s = stdInput.readLine()) != null) {
|
||||
Log.d(TAG, s);
|
||||
output.append(s).append("\n");
|
||||
}
|
||||
|
||||
// read any errors from the attempted command
|
||||
while ((s = stdError.readLine()) != null) {
|
||||
Log.e(TAG, "ERROR: " + s);
|
||||
output.append("ERROR: ").append(s).append("\n");
|
||||
}
|
||||
|
||||
process.waitFor();
|
||||
Log.d(TAG, "Finished executing binary");
|
||||
} catch (IOException e){
|
||||
Log.e(TAG, "Could not execute binary ", e);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Interrupted", e);
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
// Legacy method to get asset path.
|
||||
// This will not work on more recent Android releases.
|
||||
private String getExecutablePathFromAssets() {
|
||||
AssetManager assetManager = getAssets();
|
||||
|
||||
String abi = Build.SUPPORTED_ABIS[0];
|
||||
String extraStringForDebugBuilds = "-hwasan";
|
||||
if (abi.endsWith(extraStringForDebugBuilds)) {
|
||||
abi = abi.substring(0, abi.length() - extraStringForDebugBuilds.length());
|
||||
}
|
||||
String filesDir = getFilesDir().getPath();
|
||||
String testBinaryPath = abi + "/" + TEST_BINARY_FILENAME;
|
||||
|
||||
try {
|
||||
InputStream inStream = assetManager.open(testBinaryPath);
|
||||
Log.d(TAG, "Opened " + testBinaryPath);
|
||||
|
||||
// Copy this file to an executable location
|
||||
File outFile = new File(filesDir, TEST_BINARY_FILENAME);
|
||||
|
||||
OutputStream outStream = new FileOutputStream(outFile);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = inStream.read(buffer)) != -1) {
|
||||
outStream.write(buffer, 0, read);
|
||||
}
|
||||
inStream.close();
|
||||
outStream.flush();
|
||||
outStream.close();
|
||||
Log.d(TAG, "Copied " + testBinaryPath + " to " + filesDir);
|
||||
|
||||
String executablePath = filesDir + "/" + TEST_BINARY_FILENAME;
|
||||
Log.d(TAG, "Setting execute permission on " + executablePath);
|
||||
boolean success = new File(executablePath).setExecutable(true, false);
|
||||
if (!success) {
|
||||
Log.d(TAG, "Could not set execute permission on " + executablePath);
|
||||
}
|
||||
return executablePath;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean isRecordPermissionGranted() {
|
||||
return (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
|
||||
PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
||||
private void requestPermissions(){
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
new String[]{Manifest.permission.RECORD_AUDIO},
|
||||
APP_PERMISSION_REQUEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
|
||||
if (APP_PERMISSION_REQUEST != requestCode) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
return;
|
||||
}
|
||||
|
||||
if (grantResults.length != 1 ||
|
||||
grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
// User denied the permission, without this we cannot record audio
|
||||
// Show a toast and update the status accordingly
|
||||
outputText.setText(R.string.status_record_audio_denied);
|
||||
Toast.makeText(getApplicationContext(),
|
||||
getString(R.string.need_record_audio_permission),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
// Permission was granted, run the command
|
||||
outputText.setText(R.string.status_record_audio_granted);
|
||||
runCommand();
|
||||
}
|
||||
}
|
||||
|
||||
class UnitTestCommand implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final String output = executeBinary();
|
||||
|
||||
runOnUiThread(() -> {
|
||||
outputText.setText(output);
|
||||
|
||||
// Scroll to the bottom so we can see the test result
|
||||
scrollView.postDelayed(() -> scrollView.scrollTo(0, outputText.getBottom()), 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
34
externals/oboe/tests/UnitTestRunner/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
170
externals/oboe/tests/UnitTestRunner/app/src/main/res/drawable/ic_launcher_background.xml
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
31
externals/oboe/tests/UnitTestRunner/app/src/main/res/layout/activity_main.xml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scroll_view"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/output_view_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="bottom"
|
||||
android:text="Running tests..."
|
||||
/>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
5
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
5
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-hdpi/ic_launcher.png
vendored
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
vendored
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-mdpi/ic_launcher.png
vendored
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
vendored
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-xhdpi/ic_launcher.png
vendored
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
vendored
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
vendored
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
vendored
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
vendored
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
externals/oboe/tests/UnitTestRunner/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
vendored
Normal file
After Width: | Height: | Size: 15 KiB |
6
externals/oboe/tests/UnitTestRunner/app/src/main/res/values/colors.xml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
</resources>
|
6
externals/oboe/tests/UnitTestRunner/app/src/main/res/values/strings.xml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<string name="app_name">Unit Test Runner</string>
|
||||
<string name="need_record_audio_permission">"This app needs RECORD_AUDIO permission"</string>
|
||||
<string name="status_record_audio_denied">Error: Permission for RECORD_AUDIO was denied</string>
|
||||
<string name="status_record_audio_granted">RECORD_AUDIO permission granted. Running tests...</string>
|
||||
</resources>
|
11
externals/oboe/tests/UnitTestRunner/app/src/main/res/values/styles.xml
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
29
externals/oboe/tests/UnitTestRunner/build.gradle
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'https://maven.google.com/'
|
||||
name 'Google'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.5.1'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'https://maven.google.com/'
|
||||
name 'Google'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
22
externals/oboe/tests/UnitTestRunner/gradle.properties
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
BIN
externals/oboe/tests/UnitTestRunner/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
externals/oboe/tests/UnitTestRunner/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
#Thu Sep 27 15:12:10 BST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
172
externals/oboe/tests/UnitTestRunner/gradlew
vendored
Executable file
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
84
externals/oboe/tests/UnitTestRunner/gradlew.bat
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
externals/oboe/tests/UnitTestRunner/settings.gradle
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
include ':app'
|
158
externals/oboe/tests/run_tests.sh
vendored
Executable file
|
@ -0,0 +1,158 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright 2018 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
################################################
|
||||
# Script to build and run the Oboe tests on an attached Android device or emulator
|
||||
#
|
||||
# Prerequisites:
|
||||
# - CMake on PATH. This is usually found in $ANDROID_HOME/cmake/<version>/bin.
|
||||
# - ANDROID_NDK environment variable is set to your Android NDK location
|
||||
# e.g. $HOME/Library/Android/sdk/ndk/<version>
|
||||
# - Android device or emulator attached and accessible via adb
|
||||
#
|
||||
# Instructions:
|
||||
# - Run this script
|
||||
# - Check the test results on your target device
|
||||
#
|
||||
# What does the script do?
|
||||
# - Builds a test binary for the target architecture
|
||||
# - Copies the test binary into the UnitTestRunner app
|
||||
# - Builds, installs and runs the app on the target device
|
||||
#
|
||||
# The initial run may take some time as GTest is built, subsequent runs should be much faster.
|
||||
#
|
||||
# If you want to perform a clean build just delete the 'build' folder and re-run this script. You will need to do
|
||||
# this if you change target architectures (e.g. when changing between real device and emulator)
|
||||
#
|
||||
# Why is running the tests so convoluted?
|
||||
# The tests require the RECORDING permission and on many devices (e.g Samsung) the adb user does not have this
|
||||
# permission (and `run-as` is broken). This means that the test binary must be executed by an app which has this
|
||||
# permission, hence the need for the UnitTestRunner app.
|
||||
#
|
||||
################################################
|
||||
|
||||
# Directories, paths and filenames
|
||||
BUILD_DIR=build
|
||||
CMAKE=cmake
|
||||
TEST_BINARY_FILENAME=testOboe
|
||||
TEST_RUNNER_DIR=UnitTestRunner
|
||||
TEST_RUNNER_PACKAGE_NAME=com.google.oboe.tests.unittestrunner
|
||||
TEST_RUNNER_JNILIBS_DIR=${TEST_RUNNER_DIR}/app/src/main/jniLibs
|
||||
TEST_RUNNER_ASSETS_DIR=${TEST_RUNNER_DIR}/app/src/main/assets
|
||||
|
||||
# Check prerequisites
|
||||
if [ -z "$ANDROID_NDK" ]; then
|
||||
echo "Please set ANDROID_NDK to the Android NDK folder"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! $(type -P ${CMAKE}) ]; then
|
||||
echo "${CMAKE} was not found on your path. You can install it using Android Studio using Tools->Android->SDK Manager->SDK Tools."
|
||||
echo "Once done you will need to add ${HOME}/Library/Android/sdk/cmake/<current_version>/bin to your path."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the device ABI
|
||||
ABI=$(adb shell getprop ro.product.cpu.abi | tr -d '\n\r')
|
||||
|
||||
if [ -z "$ABI" ]; then
|
||||
echo "No device ABI was set. Please ensure a device or emulator is running. You may need to unplug extra devices."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Device/emulator architecture is $ABI"
|
||||
|
||||
if [ ${ABI} == "arm64-v8a" ] || [ ${ABI} == "x86_64" ]; then
|
||||
PLATFORM=android-21
|
||||
elif [ ${ABI} == "armeabi-v7a" ] || [ ${ABI} == "x86" ]; then
|
||||
PLATFORM=android-16
|
||||
else
|
||||
echo "Unrecognised ABI: ${ABI}. Supported ABIs are: arm64-v8a, armeabi-v7a, x86_64, x86. If you feel ${ABI} should be supported please file an issue on github.com/google/oboe"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p ${BUILD_DIR}
|
||||
|
||||
echo "Cleaning up previous build because swapping phones may result in stale binaries"
|
||||
rm -r ${BUILD_DIR}
|
||||
|
||||
# Configure the build
|
||||
echo "Building tests for ${ABI} using ${PLATFORM}"
|
||||
|
||||
CMAKE_ARGS="-S. \
|
||||
-B${BUILD_DIR} \
|
||||
-DANDROID_ABI=${ABI} \
|
||||
-DANDROID_PLATFORM=${PLATFORM} \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS=-std=c++17 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \
|
||||
-DCMAKE_VERBOSE_MAKEFILE=1"
|
||||
|
||||
cmake ${CMAKE_ARGS}
|
||||
|
||||
# Perform the build
|
||||
pushd ${BUILD_DIR}
|
||||
make -j5
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Tests built successfully"
|
||||
else
|
||||
echo "Building tests FAILED"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
# Copy the binary into the jniLibs and assets folders of the unit test runner app
|
||||
# The assets folder does not work after Android R for security reasons
|
||||
# The jniLibs folder doesn't seem to work before Android O
|
||||
# Thus, copy into both
|
||||
mkdir ${TEST_RUNNER_JNILIBS_DIR}
|
||||
mkdir ${TEST_RUNNER_JNILIBS_DIR}/${ABI}
|
||||
DESTINATION_DIR=${TEST_RUNNER_JNILIBS_DIR}/${ABI}/${TEST_BINARY_FILENAME}
|
||||
echo "Copying binary to ${DESTINATION_DIR}"
|
||||
cp ${BUILD_DIR}/${TEST_BINARY_FILENAME} ${DESTINATION_DIR}.so
|
||||
mkdir ${TEST_RUNNER_ASSETS_DIR}
|
||||
mkdir ${TEST_RUNNER_ASSETS_DIR}/${ABI}
|
||||
DESTINATION_DIR=${TEST_RUNNER_ASSETS_DIR}/${ABI}/${TEST_BINARY_FILENAME}
|
||||
echo "Copying binary to ${DESTINATION_DIR}"
|
||||
cp ${BUILD_DIR}/${TEST_BINARY_FILENAME} ${DESTINATION_DIR}.so
|
||||
|
||||
# Build and install the unit test runner app
|
||||
pushd ${TEST_RUNNER_DIR}
|
||||
echo "Building test runner app"
|
||||
./gradlew assembleDebug
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Building test app FAILED"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing to device"
|
||||
./gradlew installDebug
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Installing tests FAILED"
|
||||
exit 1
|
||||
fi
|
||||
popd
|
||||
|
||||
echo "Clear logcat from before the test."
|
||||
adb logcat -c
|
||||
echo "Starting app - Check your device for test results"
|
||||
adb shell am start ${TEST_RUNNER_PACKAGE_NAME}/.MainActivity
|
||||
|
||||
sleep 1
|
||||
echo "Logging test logs and Oboe logs. Run adb logcat for complete logs."
|
||||
adb logcat ${TEST_RUNNER_PACKAGE_NAME}.MainActivity:V OboeAudio:V *:S
|
74
externals/oboe/tests/testAAudio.cpp
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
#include "../src/aaudio/AAudioLoader.h"
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
class AAudioDirect : public ::testing::Test {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @return true if AAudio NOT supported
|
||||
*/
|
||||
bool openAAudio() {
|
||||
mAAudioLoader = AAudioLoader::getInstance();
|
||||
return (mAAudioLoader->open() != 0);
|
||||
}
|
||||
|
||||
void createBuilder() {
|
||||
ASSERT_NE(mAAudioLoader, nullptr);
|
||||
ASSERT_EQ(0, mAAudioLoader->open());
|
||||
ASSERT_NE(mAAudioLoader->createStreamBuilder, nullptr);
|
||||
aaudio_result_t result = mAAudioLoader->createStreamBuilder(&mBuilder);
|
||||
ASSERT_NE(mBuilder, nullptr);
|
||||
ASSERT_EQ(result, 0);
|
||||
}
|
||||
|
||||
void openCloseStream() {
|
||||
AAudioStream *stream = nullptr;
|
||||
aaudio_result_t result = mAAudioLoader->builder_openStream(mBuilder, &stream);
|
||||
ASSERT_EQ(result, 0);
|
||||
ASSERT_NE(stream, nullptr);
|
||||
|
||||
result = mAAudioLoader->stream_close(stream);
|
||||
ASSERT_EQ(result, 0);
|
||||
}
|
||||
|
||||
AAudioStreamBuilder *mBuilder = nullptr;
|
||||
AAudioLoader *mAAudioLoader = nullptr;
|
||||
};
|
||||
|
||||
TEST_F(AAudioDirect, InstantiateAAudioLoader) {
|
||||
AAudioLoader *aaudioLoader = AAudioLoader::getInstance();
|
||||
ASSERT_NE(aaudioLoader, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(AAudioDirect, OpenCloseHighLatencyStream) {
|
||||
if (openAAudio()) return;
|
||||
createBuilder();
|
||||
mAAudioLoader->builder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
|
||||
openCloseStream();
|
||||
}
|
||||
|
||||
TEST_F(AAudioDirect, OpenCloseLowLatencyStream) {
|
||||
if (openAAudio()) return;
|
||||
createBuilder();
|
||||
mAAudioLoader->builder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
|
||||
openCloseStream();
|
||||
}
|
92
externals/oboe/tests/testAudioClock.cpp
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Test FlowGraph
|
||||
*/
|
||||
|
||||
#include "math.h"
|
||||
#include "stdio.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
#define NANOS_PER_MICROSECOND ((int64_t) 1000)
|
||||
|
||||
constexpr int64_t kSleepTimeMicroSec = 50 * 1000;
|
||||
constexpr double kMaxLatenessMicroSec = 20 * 1000;
|
||||
|
||||
TEST(TestAudioClock, GetNanosecondsMonotonic) {
|
||||
|
||||
int64_t startNanos = AudioClock::getNanoseconds(CLOCK_MONOTONIC);
|
||||
usleep(kSleepTimeMicroSec);
|
||||
int64_t endNanos = AudioClock::getNanoseconds(CLOCK_MONOTONIC);
|
||||
ASSERT_GE(endNanos, startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond);
|
||||
ASSERT_LT(endNanos, startNanos + ((kSleepTimeMicroSec + kMaxLatenessMicroSec)
|
||||
* kNanosPerMicrosecond));
|
||||
}
|
||||
|
||||
TEST(TestAudioClock, GetNanosecondsRealtime) {
|
||||
|
||||
int64_t startNanos = AudioClock::getNanoseconds(CLOCK_REALTIME);
|
||||
usleep(kSleepTimeMicroSec);
|
||||
int64_t endNanos = AudioClock::getNanoseconds(CLOCK_REALTIME);
|
||||
ASSERT_GE(endNanos, startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond);
|
||||
ASSERT_LT(endNanos, startNanos + ((kSleepTimeMicroSec + kMaxLatenessMicroSec)
|
||||
* kNanosPerMicrosecond));
|
||||
}
|
||||
|
||||
TEST(TestAudioClock, SleepUntilNanoTimeMonotonic) {
|
||||
|
||||
int64_t startNanos = AudioClock::getNanoseconds(CLOCK_MONOTONIC);
|
||||
AudioClock::sleepUntilNanoTime(startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond, CLOCK_MONOTONIC);
|
||||
int64_t endNanos = AudioClock::getNanoseconds(CLOCK_MONOTONIC);
|
||||
ASSERT_GE(endNanos, startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond);
|
||||
ASSERT_LT(endNanos, startNanos + ((kSleepTimeMicroSec + kMaxLatenessMicroSec)
|
||||
* kNanosPerMicrosecond));
|
||||
}
|
||||
|
||||
TEST(TestAudioClock, SleepUntilNanoTimeRealtime) {
|
||||
|
||||
int64_t startNanos = AudioClock::getNanoseconds(CLOCK_REALTIME);
|
||||
AudioClock::sleepUntilNanoTime(startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond, CLOCK_REALTIME);
|
||||
int64_t endNanos = AudioClock::getNanoseconds(CLOCK_REALTIME);
|
||||
ASSERT_GE(endNanos, startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond);
|
||||
ASSERT_LT(endNanos, startNanos + ((kSleepTimeMicroSec + kMaxLatenessMicroSec)
|
||||
* kNanosPerMicrosecond));
|
||||
}
|
||||
|
||||
TEST(TestAudioClock, SleepForNanosMonotonic) {
|
||||
|
||||
int64_t startNanos = AudioClock::getNanoseconds(CLOCK_MONOTONIC);
|
||||
AudioClock::sleepForNanos(kSleepTimeMicroSec * kNanosPerMicrosecond, CLOCK_MONOTONIC);
|
||||
int64_t endNanos = AudioClock::getNanoseconds(CLOCK_MONOTONIC);
|
||||
ASSERT_GE(endNanos, startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond);
|
||||
ASSERT_LT(endNanos, startNanos + ((kSleepTimeMicroSec + kMaxLatenessMicroSec)
|
||||
* kNanosPerMicrosecond));
|
||||
}
|
||||
|
||||
TEST(TestAudioClock, SleepForNanosRealtime) {
|
||||
|
||||
int64_t startNanos = AudioClock::getNanoseconds(CLOCK_REALTIME);
|
||||
AudioClock::sleepForNanos(kSleepTimeMicroSec * kNanosPerMicrosecond, CLOCK_REALTIME);
|
||||
int64_t endNanos = AudioClock::getNanoseconds(CLOCK_REALTIME);
|
||||
ASSERT_GE(endNanos, startNanos + kSleepTimeMicroSec * kNanosPerMicrosecond);
|
||||
ASSERT_LT(endNanos, startNanos + ((kSleepTimeMicroSec + kMaxLatenessMicroSec)
|
||||
* kNanosPerMicrosecond));
|
||||
}
|
269
externals/oboe/tests/testFlowgraph.cpp
vendored
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Test FlowGraph
|
||||
*/
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
#include "flowgraph/ClipToRange.h"
|
||||
#include "flowgraph/Limiter.h"
|
||||
#include "flowgraph/MonoToMultiConverter.h"
|
||||
#include "flowgraph/SourceFloat.h"
|
||||
#include "flowgraph/RampLinear.h"
|
||||
#include "flowgraph/SinkFloat.h"
|
||||
#include "flowgraph/SinkI16.h"
|
||||
#include "flowgraph/SinkI24.h"
|
||||
#include "flowgraph/SinkI32.h"
|
||||
#include "flowgraph/SourceI16.h"
|
||||
#include "flowgraph/SourceI24.h"
|
||||
|
||||
using namespace oboe::flowgraph;
|
||||
|
||||
constexpr int kBytesPerI24Packed = 3;
|
||||
|
||||
TEST(test_flowgraph, module_sinki16) {
|
||||
static const float input[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 53.9f, -87.2f};
|
||||
static const int16_t expected[] = {32767, 16384, -8192, -32768, 0, 32767, -32768};
|
||||
int16_t output[20];
|
||||
SourceFloat sourceFloat{1};
|
||||
SinkI16 sinkI16{1};
|
||||
|
||||
int numInputFrames = sizeof(input) / sizeof(input[0]);
|
||||
sourceFloat.setData(input, numInputFrames);
|
||||
sourceFloat.output.connect(&sinkI16.input);
|
||||
|
||||
int numOutputFrames = sizeof(output) / sizeof(int16_t);
|
||||
int32_t numRead = sinkI16.read(output, numOutputFrames);
|
||||
ASSERT_EQ(numInputFrames, numRead);
|
||||
for (int i = 0; i < numRead; i++) {
|
||||
EXPECT_EQ(expected[i], output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_flowgraph, module_mono_to_stereo) {
|
||||
static const float input[] = {1.0f, 2.0f, 3.0f};
|
||||
float output[100] = {};
|
||||
SourceFloat sourceFloat{1};
|
||||
MonoToMultiConverter monoToStereo{2};
|
||||
SinkFloat sinkFloat{2};
|
||||
|
||||
sourceFloat.setData(input, 3);
|
||||
|
||||
sourceFloat.output.connect(&monoToStereo.input);
|
||||
monoToStereo.output.connect(&sinkFloat.input);
|
||||
|
||||
int32_t numRead = sinkFloat.read(output, 8);
|
||||
ASSERT_EQ(3, numRead);
|
||||
EXPECT_EQ(input[0], output[0]);
|
||||
EXPECT_EQ(input[0], output[1]);
|
||||
EXPECT_EQ(input[1], output[2]);
|
||||
EXPECT_EQ(input[1], output[3]);
|
||||
}
|
||||
|
||||
TEST(test_flowgraph, module_ramp_linear) {
|
||||
constexpr int singleNumOutput = 1;
|
||||
constexpr int rampSize = 5;
|
||||
constexpr int numOutput = 100;
|
||||
constexpr float value = 1.0f;
|
||||
constexpr float initialTarget = 10.0f;
|
||||
constexpr float finalTarget = 100.0f;
|
||||
constexpr float tolerance = 0.0001f; // arbitrary
|
||||
float output[numOutput] = {};
|
||||
RampLinear rampLinear{1};
|
||||
SinkFloat sinkFloat{1};
|
||||
|
||||
rampLinear.input.setValue(value);
|
||||
rampLinear.setLengthInFrames(rampSize);
|
||||
rampLinear.output.connect(&sinkFloat.input);
|
||||
|
||||
// Check that the values go to the initial target instantly.
|
||||
rampLinear.setTarget(initialTarget);
|
||||
int32_t singleNumRead = sinkFloat.read(output, singleNumOutput);
|
||||
ASSERT_EQ(singleNumRead, singleNumOutput);
|
||||
EXPECT_NEAR(value * initialTarget, output[0], tolerance);
|
||||
|
||||
// Now set target and check that the linear ramp works as expected.
|
||||
rampLinear.setTarget(finalTarget);
|
||||
int32_t numRead = sinkFloat.read(output, numOutput);
|
||||
const float incrementSize = (finalTarget - initialTarget) / rampSize;
|
||||
ASSERT_EQ(numOutput, numRead);
|
||||
|
||||
int i = 0;
|
||||
for (; i < rampSize; i++) {
|
||||
float expected = value * (initialTarget + i * incrementSize);
|
||||
EXPECT_NEAR(expected, output[i], tolerance);
|
||||
}
|
||||
for (; i < numOutput; i++) {
|
||||
float expected = value * finalTarget;
|
||||
EXPECT_NEAR(expected, output[i], tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
// It is easiest to represent packed 24-bit data as a byte array.
|
||||
// This test will read from input, convert to float, then write
|
||||
// back to output as bytes.
|
||||
TEST(test_flowgraph, module_packed_24) {
|
||||
static const uint8_t input[] = {0x01, 0x23, 0x45,
|
||||
0x67, 0x89, 0xAB,
|
||||
0xCD, 0xEF, 0x5A};
|
||||
uint8_t output[99] = {};
|
||||
SourceI24 sourceI24{1};
|
||||
SinkI24 sinkI24{1};
|
||||
|
||||
int numInputFrames = sizeof(input) / kBytesPerI24Packed;
|
||||
sourceI24.setData(input, numInputFrames);
|
||||
sourceI24.output.connect(&sinkI24.input);
|
||||
|
||||
int32_t numRead = sinkI24.read(output, sizeof(output) / kBytesPerI24Packed);
|
||||
ASSERT_EQ(numInputFrames, numRead);
|
||||
for (size_t i = 0; i < sizeof(input); i++) {
|
||||
EXPECT_EQ(input[i], output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_flowgraph, module_clip_to_range) {
|
||||
constexpr float myMin = -2.0f;
|
||||
constexpr float myMax = 1.5f;
|
||||
|
||||
static const float input[] = {-9.7, 0.5f, -0.25, 1.0f, 12.3};
|
||||
static const float expected[] = {myMin, 0.5f, -0.25, 1.0f, myMax};
|
||||
float output[100];
|
||||
SourceFloat sourceFloat{1};
|
||||
ClipToRange clipper{1};
|
||||
SinkFloat sinkFloat{1};
|
||||
|
||||
int numInputFrames = sizeof(input) / sizeof(input[0]);
|
||||
sourceFloat.setData(input, numInputFrames);
|
||||
|
||||
clipper.setMinimum(myMin);
|
||||
clipper.setMaximum(myMax);
|
||||
|
||||
sourceFloat.output.connect(&clipper.input);
|
||||
clipper.output.connect(&sinkFloat.input);
|
||||
|
||||
int numOutputFrames = sizeof(output) / sizeof(output[0]);
|
||||
int32_t numRead = sinkFloat.read(output, numOutputFrames);
|
||||
ASSERT_EQ(numInputFrames, numRead);
|
||||
constexpr float tolerance = 0.000001f; // arbitrary
|
||||
for (int i = 0; i < numRead; i++) {
|
||||
EXPECT_NEAR(expected[i], output[i], tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_flowgraph, module_sinki32) {
|
||||
static constexpr int kNumSamples = 8;
|
||||
static const float input[] = {
|
||||
1.0f, 0.5f, -0.25f, -1.0f,
|
||||
0.0f, 53.9f, -87.2f, -1.02f};
|
||||
static const int32_t expected[] = {
|
||||
INT32_MAX, 1 << 30, INT32_MIN / 4, INT32_MIN,
|
||||
0, INT32_MAX, INT32_MIN, INT32_MIN};
|
||||
int32_t output[kNumSamples + 10]; // larger than input
|
||||
|
||||
SourceFloat sourceFloat{1};
|
||||
SinkI32 sinkI32{1};
|
||||
|
||||
sourceFloat.setData(input, kNumSamples);
|
||||
sourceFloat.output.connect(&sinkI32.input);
|
||||
|
||||
int numOutputFrames = sizeof(output) / sizeof(int32_t);
|
||||
int32_t numRead = sinkI32.read(output, numOutputFrames);
|
||||
ASSERT_EQ(kNumSamples, numRead);
|
||||
for (int i = 0; i < numRead; i++) {
|
||||
EXPECT_EQ(expected[i], output[i]) << ", i = " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_flowgraph, module_limiter) {
|
||||
constexpr int kNumSamples = 101;
|
||||
constexpr float kLastSample = 3.0f;
|
||||
constexpr float kFirstSample = -kLastSample;
|
||||
constexpr float kDeltaBetweenSamples = (kLastSample - kFirstSample) / (kNumSamples - 1);
|
||||
constexpr float kTolerance = 0.00001f;
|
||||
|
||||
float input[kNumSamples];
|
||||
float output[kNumSamples];
|
||||
SourceFloat sourceFloat{1};
|
||||
Limiter limiter{1};
|
||||
SinkFloat sinkFloat{1};
|
||||
|
||||
for (int i = 0; i < kNumSamples; i++) {
|
||||
input[i] = kFirstSample + i * kDeltaBetweenSamples;
|
||||
}
|
||||
|
||||
const int numInputFrames = std::size(input);
|
||||
sourceFloat.setData(input, numInputFrames);
|
||||
|
||||
sourceFloat.output.connect(&limiter.input);
|
||||
limiter.output.connect(&sinkFloat.input);
|
||||
|
||||
const int numOutputFrames = std::size(output);
|
||||
int32_t numRead = sinkFloat.read(output, numOutputFrames);
|
||||
ASSERT_EQ(numInputFrames, numRead);
|
||||
|
||||
for (int i = 0; i < numRead; i++) {
|
||||
// limiter must be symmetric wrt 0.
|
||||
EXPECT_NEAR(output[i], -output[kNumSamples - i - 1], kTolerance);
|
||||
if (i > 0) {
|
||||
EXPECT_GE(output[i], output[i - 1]); // limiter must be monotonic
|
||||
}
|
||||
if (input[i] == 0.f) {
|
||||
EXPECT_EQ(0.f, output[i]);
|
||||
} else if (input[i] > 0.0f) {
|
||||
EXPECT_GE(output[i], 0.0f);
|
||||
EXPECT_LE(output[i], M_SQRT2); // limiter actually limits
|
||||
EXPECT_LE(output[i], input[i]); // a limiter, gain <= 1
|
||||
} else {
|
||||
EXPECT_LE(output[i], 0.0f);
|
||||
EXPECT_GE(output[i], -M_SQRT2); // limiter actually limits
|
||||
EXPECT_GE(output[i], input[i]); // a limiter, gain <= 1
|
||||
}
|
||||
if (-1.f <= input[i] && input[i] <= 1.f) {
|
||||
EXPECT_EQ(input[i], output[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_flowgraph, module_limiter_nan) {
|
||||
constexpr int kArbitraryOutputSize = 100;
|
||||
constexpr float kFloatNan = NAN;
|
||||
static const float input[] = {kFloatNan, 0.5f, kFloatNan, kFloatNan, -10.0f, kFloatNan};
|
||||
static const float expected[] = {0.0f, 0.5f, 0.5f, 0.5f, -M_SQRT2, -M_SQRT2};
|
||||
constexpr float tolerance = 0.00001f;
|
||||
float output[kArbitraryOutputSize];
|
||||
SourceFloat sourceFloat{1};
|
||||
Limiter limiter{1};
|
||||
SinkFloat sinkFloat{1};
|
||||
|
||||
const int numInputFrames = std::size(input);
|
||||
sourceFloat.setData(input, numInputFrames);
|
||||
|
||||
sourceFloat.output.connect(&limiter.input);
|
||||
limiter.output.connect(&sinkFloat.input);
|
||||
|
||||
const int numOutputFrames = std::size(output);
|
||||
int32_t numRead = sinkFloat.read(output, numOutputFrames);
|
||||
ASSERT_EQ(numInputFrames, numRead);
|
||||
|
||||
for (int i = 0; i < numRead; i++) {
|
||||
EXPECT_NEAR(expected[i], output[i], tolerance);
|
||||
}
|
||||
}
|
206
externals/oboe/tests/testFullDuplexStream.cpp
vendored
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
static constexpr int kTimeToSleepMicros = 5 * 1000 * 1000; // 5 s
|
||||
|
||||
using TestFullDuplexStreamParams = std::tuple<AudioApi, PerformanceMode, AudioApi, PerformanceMode>;
|
||||
|
||||
class TestFullDuplexStream : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<TestFullDuplexStreamParams>,
|
||||
public FullDuplexStream {
|
||||
public:
|
||||
DataCallbackResult onBothStreamsReady(
|
||||
const void *inputData,
|
||||
int numInputFrames,
|
||||
void *outputData,
|
||||
int numOutputFrames) override {
|
||||
mCallbackCount++;
|
||||
if (numInputFrames == numOutputFrames) {
|
||||
mGoodCallbackCount++;
|
||||
}
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void openStream(AudioApi inputAudioApi, PerformanceMode inputPerfMode,
|
||||
AudioApi outputAudioApi, PerformanceMode outputPerfMode) {
|
||||
mOutputBuilder.setDirection(Direction::Output);
|
||||
if (mOutputBuilder.isAAudioRecommended()) {
|
||||
mOutputBuilder.setAudioApi(outputAudioApi);
|
||||
}
|
||||
mOutputBuilder.setPerformanceMode(outputPerfMode);
|
||||
mOutputBuilder.setChannelCount(1);
|
||||
mOutputBuilder.setFormat(AudioFormat::Float);
|
||||
mOutputBuilder.setDataCallback(this);
|
||||
|
||||
Result r = mOutputBuilder.openStream(mOutputStream);
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to open output stream " << convertToText(r);
|
||||
|
||||
mInputBuilder.setDirection(Direction::Input);
|
||||
if (mInputBuilder.isAAudioRecommended()) {
|
||||
mInputBuilder.setAudioApi(inputAudioApi);
|
||||
}
|
||||
mInputBuilder.setPerformanceMode(inputPerfMode);
|
||||
mInputBuilder.setChannelCount(1);
|
||||
mInputBuilder.setFormat(AudioFormat::Float);
|
||||
mInputBuilder.setBufferCapacityInFrames(mOutputStream->getBufferCapacityInFrames() * 2);
|
||||
mInputBuilder.setSampleRate(mOutputStream->getSampleRate());
|
||||
|
||||
r = mInputBuilder.openStream(mInputStream);
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to open input stream " << convertToText(r);
|
||||
|
||||
setSharedInputStream(mInputStream);
|
||||
setSharedOutputStream(mOutputStream);
|
||||
}
|
||||
|
||||
void startStream() {
|
||||
Result r = start();
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to start streams " << convertToText(r);
|
||||
}
|
||||
|
||||
void stopStream() {
|
||||
Result r = stop();
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to stop streams " << convertToText(r);
|
||||
}
|
||||
|
||||
void closeStream() {
|
||||
Result r = mOutputStream->close();
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to close output stream " << convertToText(r);
|
||||
r = mInputStream->close();
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to close input stream " << convertToText(r);
|
||||
}
|
||||
|
||||
void checkXRuns() {
|
||||
// Expect few xRuns with the use of full duplex stream
|
||||
EXPECT_LT(mInputStream->getXRunCount().value(), 10);
|
||||
EXPECT_LT(mOutputStream->getXRunCount().value(), 10);
|
||||
}
|
||||
|
||||
void checkInputAndOutputBufferSizesMatch() {
|
||||
// Expect the large majority of callbacks to have the same sized input and output
|
||||
EXPECT_GE(mGoodCallbackCount, mCallbackCount * 4 / 5);
|
||||
}
|
||||
|
||||
AudioStreamBuilder mInputBuilder;
|
||||
AudioStreamBuilder mOutputBuilder;
|
||||
std::shared_ptr<AudioStream> mInputStream;
|
||||
std::shared_ptr<AudioStream> mOutputStream;
|
||||
std::atomic<int32_t> mCallbackCount{0};
|
||||
std::atomic<int32_t> mGoodCallbackCount{0};
|
||||
};
|
||||
|
||||
TEST_P(TestFullDuplexStream, VerifyFullDuplexStream) {
|
||||
const AudioApi inputAudioApi = std::get<0>(GetParam());
|
||||
const PerformanceMode inputPerformanceMode = std::get<1>(GetParam());
|
||||
const AudioApi outputAudioApi = std::get<2>(GetParam());
|
||||
const PerformanceMode outputPerformanceMode = std::get<3>(GetParam());
|
||||
|
||||
openStream(inputAudioApi, inputPerformanceMode, outputAudioApi, outputPerformanceMode);
|
||||
startStream();
|
||||
usleep(kTimeToSleepMicros);
|
||||
checkXRuns();
|
||||
checkInputAndOutputBufferSizesMatch();
|
||||
stopStream();
|
||||
closeStream();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestFullDuplexStreamTest,
|
||||
TestFullDuplexStream,
|
||||
::testing::Values(
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
|
||||
AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
|
||||
AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
|
||||
AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
|
||||
AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
|
||||
AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
|
||||
AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
|
||||
AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
|
||||
AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
|
||||
AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
|
||||
AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
|
||||
AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
|
||||
AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
|
||||
AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
|
||||
AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
|
||||
AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
|
||||
AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
|
||||
AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
|
||||
AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
|
||||
AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
|
||||
AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
|
||||
AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
|
||||
AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
|
||||
AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
|
||||
AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
|
||||
AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
|
||||
AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
|
||||
AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
|
||||
AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
|
||||
AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
|
||||
AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
|
||||
AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
|
||||
AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
|
||||
AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
|
||||
AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
|
||||
AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
|
||||
AudioApi::OpenSLES, PerformanceMode::PowerSaving})
|
||||
)
|
||||
);
|
225
externals/oboe/tests/testResampler.cpp
vendored
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Test FlowGraph
|
||||
*/
|
||||
|
||||
#include "math.h"
|
||||
#include "stdio.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
#include "flowgraph/resampler/MultiChannelResampler.h"
|
||||
|
||||
using namespace oboe::resampler;
|
||||
|
||||
// Measure zero crossings.
|
||||
static int32_t countZeroCrossingsWithHysteresis(float *input, int32_t numSamples) {
|
||||
const float kHysteresisLevel = 0.25f;
|
||||
int zeroCrossingCount = 0;
|
||||
int state = 0; // can be -1, 0, +1
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
if (input[i] >= kHysteresisLevel) {
|
||||
if (state < 0) {
|
||||
zeroCrossingCount++;
|
||||
}
|
||||
state = 1;
|
||||
} else if (input[i] <= -kHysteresisLevel) {
|
||||
if (state > 0) {
|
||||
zeroCrossingCount++;
|
||||
}
|
||||
state = -1;
|
||||
}
|
||||
}
|
||||
return zeroCrossingCount;
|
||||
}
|
||||
|
||||
static constexpr int kChannelCount = 1;
|
||||
|
||||
/**
|
||||
* Convert a sine wave and then look for glitches.
|
||||
* Glitches have a high value in the second derivative.
|
||||
*/
|
||||
static void checkResampler(int32_t sourceRate, int32_t sinkRate,
|
||||
MultiChannelResampler::Quality quality) {
|
||||
const int kNumOutputSamples = 10000;
|
||||
const double framesPerCycle = 81.379; // target output period
|
||||
|
||||
int numInputSamples = kNumOutputSamples * sourceRate / sinkRate;
|
||||
|
||||
std::unique_ptr<float[]> inputBuffer = std::make_unique<float[]>(numInputSamples);
|
||||
std::unique_ptr<float[]> outputBuffer = std::make_unique<float[]>(kNumOutputSamples);
|
||||
|
||||
// Generate a sine wave for input.
|
||||
const double kPhaseIncrement = 2.0 * sinkRate / (framesPerCycle * sourceRate);
|
||||
double phase = 0.0;
|
||||
for (int i = 0; i < numInputSamples; i++) {
|
||||
inputBuffer[i] = sin(phase * M_PI);
|
||||
phase += kPhaseIncrement;
|
||||
while (phase > 1.0) {
|
||||
phase -= 2.0;
|
||||
}
|
||||
}
|
||||
int sourceZeroCrossingCount = countZeroCrossingsWithHysteresis(inputBuffer.get(), numInputSamples);
|
||||
|
||||
// Use a MultiChannelResampler to convert from the sourceRate to the sinkRate.
|
||||
std::unique_ptr<MultiChannelResampler> mcResampler;
|
||||
mcResampler.reset(MultiChannelResampler::make(kChannelCount,
|
||||
sourceRate,
|
||||
sinkRate,
|
||||
quality));
|
||||
int inputFramesLeft = numInputSamples;
|
||||
int numRead = 0;
|
||||
float *input = inputBuffer.get(); // for iteration
|
||||
float *output = outputBuffer.get();
|
||||
while (inputFramesLeft > 0) {
|
||||
if (mcResampler->isWriteNeeded()) {
|
||||
mcResampler->writeNextFrame(input);
|
||||
input++;
|
||||
inputFramesLeft--;
|
||||
} else {
|
||||
mcResampler->readNextFrame(output);
|
||||
output++;
|
||||
numRead++;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush out remaining frames from the flowgraph
|
||||
while (!mcResampler->isWriteNeeded()) {
|
||||
mcResampler->readNextFrame(output);
|
||||
output++;
|
||||
numRead++;
|
||||
}
|
||||
|
||||
ASSERT_LE(numRead, kNumOutputSamples);
|
||||
// Some frames are lost priming the FIR filter.
|
||||
const int kMaxAlgorithmicFrameLoss = 5;
|
||||
EXPECT_GT(numRead, kNumOutputSamples - kMaxAlgorithmicFrameLoss);
|
||||
|
||||
int sinkZeroCrossingCount = countZeroCrossingsWithHysteresis(outputBuffer.get(), numRead);
|
||||
// The sine wave may be cut off partially. This may cause multiple crossing
|
||||
// differences when upsampling.
|
||||
const int kMaxZeroCrossingDelta = std::max(sinkRate / sourceRate / 2, 1);
|
||||
EXPECT_LE(abs(sourceZeroCrossingCount - sinkZeroCrossingCount), kMaxZeroCrossingDelta);
|
||||
|
||||
// Detect glitches by looking for spikes in the second derivative.
|
||||
output = outputBuffer.get();
|
||||
float previousValue = output[0];
|
||||
float previousSlope = output[1] - output[0];
|
||||
for (int i = 0; i < numRead; i++) {
|
||||
float slope = output[i] - previousValue;
|
||||
float slopeDelta = fabs(slope - previousSlope);
|
||||
// Skip a few samples because there are often some steep slope changes at the beginning.
|
||||
if (i > 10) {
|
||||
EXPECT_LT(slopeDelta, 0.1);
|
||||
}
|
||||
previousValue = output[i];
|
||||
previousSlope = slope;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Save to disk for inspection.
|
||||
FILE *fp = fopen( "/sdcard/Download/src_float_out.raw" , "wb" );
|
||||
fwrite(outputBuffer.get(), sizeof(float), numRead, fp );
|
||||
fclose(fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TEST(test_resampler, resampler_scan_all) {
|
||||
const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000, 64000, 88200, 96000};
|
||||
const MultiChannelResampler::Quality qualities[] =
|
||||
{
|
||||
MultiChannelResampler::Quality::Fastest,
|
||||
MultiChannelResampler::Quality::Low,
|
||||
MultiChannelResampler::Quality::Medium,
|
||||
MultiChannelResampler::Quality::High,
|
||||
MultiChannelResampler::Quality::Best
|
||||
};
|
||||
for (int srcRate : rates) {
|
||||
for (int destRate : rates) {
|
||||
for (auto quality : qualities) {
|
||||
if (srcRate != destRate) {
|
||||
checkResampler(srcRate, destRate, quality);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_8000_11025_best) {
|
||||
checkResampler(8000, 11025, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
TEST(test_resampler, resampler_8000_48000_best) {
|
||||
checkResampler(8000, 48000, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_8000_44100_best) {
|
||||
checkResampler(8000, 44100, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_11025_24000_best) {
|
||||
checkResampler(11025, 24000, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_11025_48000_fastest) {
|
||||
checkResampler(11025, 48000, MultiChannelResampler::Quality::Fastest);
|
||||
}
|
||||
TEST(test_resampler, resampler_11025_48000_low) {
|
||||
checkResampler(11025, 48000, MultiChannelResampler::Quality::Low);
|
||||
}
|
||||
TEST(test_resampler, resampler_11025_48000_medium) {
|
||||
checkResampler(11025, 48000, MultiChannelResampler::Quality::Medium);
|
||||
}
|
||||
TEST(test_resampler, resampler_11025_48000_high) {
|
||||
checkResampler(11025, 48000, MultiChannelResampler::Quality::High);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_11025_48000_best) {
|
||||
checkResampler(11025, 48000, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_11025_44100_best) {
|
||||
checkResampler(11025, 44100, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_11025_88200_best) {
|
||||
checkResampler(11025, 88200, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_16000_48000_best) {
|
||||
checkResampler(16000, 48000, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
TEST(test_resampler, resampler_44100_48000_low) {
|
||||
checkResampler(44100, 48000, MultiChannelResampler::Quality::Low);
|
||||
}
|
||||
TEST(test_resampler, resampler_44100_48000_best) {
|
||||
checkResampler(44100, 48000, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
|
||||
// Look for glitches when downsampling.
|
||||
TEST(test_resampler, resampler_48000_11025_best) {
|
||||
checkResampler(48000, 11025, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
TEST(test_resampler, resampler_48000_44100_best) {
|
||||
checkResampler(48000, 44100, MultiChannelResampler::Quality::Best);
|
||||
}
|
||||
TEST(test_resampler, resampler_44100_11025_best) {
|
||||
checkResampler(44100, 11025, MultiChannelResampler::Quality::Best);
|
||||
}
|
133
externals/oboe/tests/testReturnStop.cpp
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <tuple>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
|
||||
// Test returning DataCallbackResult::Stop from a callback.
|
||||
using namespace oboe;
|
||||
|
||||
static constexpr int kTimeoutInNanos = 500 * kNanosPerMillisecond;
|
||||
|
||||
class ReturnStopCallback : public AudioStreamDataCallback {
|
||||
public:
|
||||
DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
|
||||
return (++callbackCount < kMaxCallbacks) ? DataCallbackResult::Continue : DataCallbackResult::Stop;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
callbackCount = 0;
|
||||
}
|
||||
|
||||
int getMaxCallbacks() const { return kMaxCallbacks; }
|
||||
|
||||
std::atomic<int> callbackCount{0};
|
||||
|
||||
private:
|
||||
// I get strange linker errors with GTest if I try to reference this directly.
|
||||
static constexpr int kMaxCallbacks = 40;
|
||||
};
|
||||
|
||||
using StreamReturnStopParams = std::tuple<Direction, AudioApi, PerformanceMode, bool>;
|
||||
|
||||
class StreamReturnStop : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<StreamReturnStopParams> {
|
||||
|
||||
protected:
|
||||
void TearDown() override;
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
};
|
||||
|
||||
void StreamReturnStop::TearDown() {
|
||||
if (mStream) {
|
||||
mStream->close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StreamReturnStop, VerifyStreamReturnStop) {
|
||||
const Direction direction = std::get<0>(GetParam());
|
||||
const AudioApi audioApi = std::get<1>(GetParam());
|
||||
const PerformanceMode performanceMode = std::get<2>(GetParam());
|
||||
const bool useRequestStart = std::get<3>(GetParam());
|
||||
|
||||
ReturnStopCallback *callback = new ReturnStopCallback();
|
||||
mBuilder.setDirection(direction)
|
||||
->setFormat(AudioFormat::I16)
|
||||
->setPerformanceMode(performanceMode)
|
||||
->setDataCallback(callback);
|
||||
if (mBuilder.isAAudioRecommended()) {
|
||||
mBuilder.setAudioApi(audioApi);
|
||||
}
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to open stream. " << convertToText(r);
|
||||
|
||||
// Start and stop several times.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
callback->reset();
|
||||
// Oboe has two ways to start a stream.
|
||||
if (useRequestStart) {
|
||||
r = mStream->requestStart();
|
||||
} else {
|
||||
r = mStream->start();
|
||||
}
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to start stream. " << convertToText(r);
|
||||
|
||||
// Wait for callbacks to complete.
|
||||
const int kMaxCallbackPeriodMillis = 500;
|
||||
const int kPollPeriodMillis = 20;
|
||||
int timeout = 2 * callback->getMaxCallbacks() * kMaxCallbackPeriodMillis / kPollPeriodMillis;
|
||||
do {
|
||||
usleep(kPollPeriodMillis * 1000);
|
||||
} while (callback->callbackCount < callback->getMaxCallbacks() && timeout-- > 0);
|
||||
EXPECT_GT(timeout, 0) << "timed out waiting for enough callbacks";
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
r = mStream->waitForStateChange(StreamState::Started, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK) << "waitForStateChange(Started) timed out. " << convertToText(r);
|
||||
r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK) << "waitForStateChange(Stopping) timed out. " << convertToText(r);
|
||||
EXPECT_EQ(next, StreamState::Stopped) << "Stream not in state Stopped, was " << convertToText(next);
|
||||
|
||||
EXPECT_EQ(callback->callbackCount, callback->getMaxCallbacks()) << "Too many callbacks = " << callback->callbackCount;
|
||||
|
||||
const int kOboeStartStopSleepMSec = 10;
|
||||
usleep(kOboeStartStopSleepMSec * 1000); // avoid race condition in emulator
|
||||
}
|
||||
|
||||
ASSERT_EQ(Result::OK, mStream->close());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
StreamReturnStopTest,
|
||||
StreamReturnStop,
|
||||
::testing::Values(
|
||||
// Last boolean is true if requestStart() should be called instead of start().
|
||||
StreamReturnStopParams({Direction::Output, AudioApi::AAudio, PerformanceMode::LowLatency, true}),
|
||||
StreamReturnStopParams({Direction::Output, AudioApi::AAudio, PerformanceMode::LowLatency, false}),
|
||||
StreamReturnStopParams({Direction::Output, AudioApi::AAudio, PerformanceMode::None, true}),
|
||||
StreamReturnStopParams({Direction::Output, AudioApi::AAudio, PerformanceMode::None, false}),
|
||||
StreamReturnStopParams({Direction::Output, AudioApi::OpenSLES, PerformanceMode::LowLatency, true}),
|
||||
StreamReturnStopParams({Direction::Output, AudioApi::OpenSLES, PerformanceMode::LowLatency, false}),
|
||||
StreamReturnStopParams({Direction::Input, AudioApi::AAudio, PerformanceMode::LowLatency, true}),
|
||||
StreamReturnStopParams({Direction::Input, AudioApi::AAudio, PerformanceMode::LowLatency, false})
|
||||
)
|
||||
);
|
161
externals/oboe/tests/testReturnStopDeadlock.cpp
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <tuple>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
|
||||
// Test returning DataCallbackResult::Stop from a callback.
|
||||
using namespace oboe;
|
||||
|
||||
// Test whether there is a deadlock when stopping streams.
|
||||
// See Issue #2059
|
||||
|
||||
class TestReturnStopDeadlock : public ::testing::Test {
|
||||
public:
|
||||
|
||||
void start(bool useOpenSL);
|
||||
void stop();
|
||||
|
||||
int32_t getCycleCount() {
|
||||
return mCycleCount.load();
|
||||
}
|
||||
|
||||
protected:
|
||||
void TearDown() override;
|
||||
|
||||
private:
|
||||
|
||||
void cycleRapidly(bool useOpenSL);
|
||||
|
||||
class MyDataCallback : public oboe::AudioStreamDataCallback { public:
|
||||
|
||||
MyDataCallback() {}
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(
|
||||
oboe::AudioStream *audioStream,
|
||||
void *audioData,
|
||||
int32_t numFrames) override;
|
||||
|
||||
std::atomic<bool> returnStop = false;
|
||||
std::atomic<int32_t> callbackCount{0};
|
||||
};
|
||||
|
||||
std::shared_ptr<oboe::AudioStream> mStream;
|
||||
std::shared_ptr<MyDataCallback> mDataCallback;
|
||||
std::atomic<int32_t> mCycleCount{0};
|
||||
std::atomic<bool> mThreadEnabled{false};
|
||||
std::thread mCycleThread;
|
||||
|
||||
static constexpr int kChannelCount = 1;
|
||||
static constexpr int kMaxSleepMicros = 25000;
|
||||
};
|
||||
|
||||
// start a thread to cycle through stream tests
|
||||
void TestReturnStopDeadlock::start(bool useOpenSL) {
|
||||
mThreadEnabled = true;
|
||||
mCycleCount = 0;
|
||||
mCycleThread = std::thread([this, useOpenSL]() {
|
||||
cycleRapidly(useOpenSL);
|
||||
});
|
||||
}
|
||||
|
||||
void TestReturnStopDeadlock::stop() {
|
||||
mThreadEnabled = false;
|
||||
// Terminate the thread with a timeout.
|
||||
const int timeout = 1;
|
||||
auto future = std::async(std::launch::async, &std::thread::join, &mCycleThread);
|
||||
ASSERT_NE(future.wait_for(std::chrono::seconds(timeout)), std::future_status::timeout)
|
||||
<< " join() timed out! cycles = " << getCycleCount();
|
||||
}
|
||||
|
||||
void TestReturnStopDeadlock::TearDown() {
|
||||
if (mStream) {
|
||||
mStream->close();
|
||||
}
|
||||
}
|
||||
|
||||
void TestReturnStopDeadlock::cycleRapidly(bool useOpenSL) {
|
||||
while(mThreadEnabled) {
|
||||
mCycleCount++;
|
||||
mDataCallback = std::make_shared<MyDataCallback>();
|
||||
|
||||
AudioStreamBuilder builder;
|
||||
oboe::Result result = builder.setFormat(oboe::AudioFormat::Float)
|
||||
->setAudioApi(useOpenSL ? oboe::AudioApi::OpenSLES : oboe::AudioApi::AAudio)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setChannelCount(kChannelCount)
|
||||
->setDataCallback(mDataCallback)
|
||||
->setUsage(oboe::Usage::Notification)
|
||||
->openStream(mStream);
|
||||
ASSERT_EQ(result, oboe::Result::OK);
|
||||
|
||||
mStream->setDelayBeforeCloseMillis(0);
|
||||
|
||||
result = mStream->requestStart();
|
||||
ASSERT_EQ(result, oboe::Result::OK);
|
||||
|
||||
// Sleep for some random time.
|
||||
int countdown = 100;
|
||||
while ((mDataCallback->callbackCount < 4) && (--countdown > 0)) {
|
||||
int32_t durationMicros = (int32_t)(drand48() * kMaxSleepMicros);
|
||||
usleep(durationMicros);
|
||||
}
|
||||
mDataCallback->returnStop = true;
|
||||
result = mStream->close();
|
||||
ASSERT_EQ(result, oboe::Result::OK);
|
||||
mStream = nullptr;
|
||||
ASSERT_GT(mDataCallback->callbackCount, 1) << " cycleCount = " << mCycleCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Callback that returns Continue or Stop
|
||||
DataCallbackResult TestReturnStopDeadlock::MyDataCallback::onAudioReady(
|
||||
AudioStream *audioStream,
|
||||
void *audioData,
|
||||
int32_t numFrames) {
|
||||
float *floatData = (float *) audioData;
|
||||
const int numSamples = numFrames * kChannelCount;
|
||||
callbackCount++;
|
||||
|
||||
// Fill buffer with white noise.
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
floatData[i] = ((float) drand48() - 0.5f) * 2 * 0.1f;
|
||||
}
|
||||
usleep(500); // half a millisecond
|
||||
if (returnStop) {
|
||||
usleep(20 * 1000);
|
||||
return DataCallbackResult::Stop;
|
||||
} else {
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestReturnStopDeadlock, RapidCycleAAudio){
|
||||
start(false);
|
||||
usleep(3000 * 1000);
|
||||
stop();
|
||||
}
|
||||
|
||||
TEST_F(TestReturnStopDeadlock, RapidCycleOpenSL){
|
||||
start(true);
|
||||
usleep(3000 * 1000);
|
||||
stop();
|
||||
}
|
432
externals/oboe/tests/testStreamClosedMethods.cpp
vendored
Normal file
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
class MyCallback : public AudioStreamDataCallback {
|
||||
public:
|
||||
DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
};
|
||||
|
||||
class StreamClosedReturnValues : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
|
||||
bool openStream() {
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
bool releaseStream() {
|
||||
Result r = mStream->release();
|
||||
if (getSdkVersion() > __ANDROID_API_R__ && mBuilder.getAudioApi() != AudioApi::OpenSLES) {
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to release stream. " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
} else {
|
||||
EXPECT_EQ(r, Result::ErrorUnimplemented) << "Did not get ErrorUnimplemented" << convertToText(r);
|
||||
return (r == Result::ErrorUnimplemented);
|
||||
}
|
||||
}
|
||||
|
||||
bool closeStream() {
|
||||
Result r = mStream->close();
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to close stream. " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
bool openAndCloseStream() {
|
||||
if (!openStream() || !closeStream())
|
||||
return false;
|
||||
StreamState s = mStream->getState();
|
||||
EXPECT_EQ(s, StreamState::Closed) << "Stream state " << convertToText(mStream->getState());
|
||||
return (s == StreamState::Closed);
|
||||
}
|
||||
|
||||
static int64_t getNanoseconds() {
|
||||
struct timespec time;
|
||||
int result = clock_gettime(CLOCK_MONOTONIC, &time);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
return (time.tv_sec * (int64_t)1e9) + time.tv_nsec;
|
||||
}
|
||||
|
||||
// ASSERT_* requires a void return type.
|
||||
void measureCloseTime(int32_t delayMillis) {
|
||||
ASSERT_TRUE(openStream());
|
||||
mStream->setDelayBeforeCloseMillis(delayMillis);
|
||||
ASSERT_EQ(delayMillis, mStream->getDelayBeforeCloseMillis());
|
||||
// Measure time it takes to close.
|
||||
int64_t startTimeMillis = getNanoseconds() / 1e6;
|
||||
ASSERT_TRUE(closeStream());
|
||||
int64_t stopTimeMillis = getNanoseconds() / 1e6;
|
||||
int32_t elapsedTimeMillis = (int32_t)(stopTimeMillis - startTimeMillis);
|
||||
ASSERT_GE(elapsedTimeMillis, delayMillis);
|
||||
}
|
||||
|
||||
void testDelayBeforeClose() {
|
||||
const int32_t delayMillis = 500;
|
||||
measureCloseTime(delayMillis);
|
||||
}
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
|
||||
};
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetChannelCountReturnsLastKnownValue){
|
||||
|
||||
mBuilder.setChannelCount(2);
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
ASSERT_EQ(mStream->getChannelCount(), 2);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetDirectionReturnsLastKnownValue){
|
||||
|
||||
// Note that when testing on the emulator setting the direction to Input will result in ErrorInternal when
|
||||
// opening the stream
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
ASSERT_EQ(mStream->getDirection(), Direction::Input);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetSampleRateReturnsLastKnownValue){
|
||||
|
||||
mBuilder.setSampleRate(8000);
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
ASSERT_EQ(mStream->getSampleRate(), 8000);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetFramesPerCallbackReturnsLastKnownValue) {
|
||||
|
||||
mBuilder.setFramesPerCallback(192);
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
ASSERT_EQ(mStream->getFramesPerCallback(), 192);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetFormatReturnsLastKnownValue) {
|
||||
|
||||
mBuilder.setFormat(AudioFormat::I16);
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetBufferSizeInFramesReturnsLastKnownValue) {
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
int32_t bufferSize = mStream->getBufferSizeInFrames();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getBufferSizeInFrames(), bufferSize);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetBufferCapacityInFramesReturnsLastKnownValue) {
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
int32_t bufferCapacity = mStream->getBufferCapacityInFrames();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getBufferCapacityInFrames(), bufferCapacity);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetSharingModeReturnsLastKnownValue) {
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
SharingMode s = mStream->getSharingMode();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getSharingMode(), s);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetPerformanceModeReturnsLastKnownValue) {
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
PerformanceMode p = mStream->getPerformanceMode();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getPerformanceMode(), p);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetDeviceIdReturnsLastKnownValue) {
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
int32_t d = mStream->getDeviceId();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getDeviceId(), d);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetDataCallbackReturnsLastKnownValue) {
|
||||
|
||||
AudioStreamDataCallback *callback = new MyCallback();
|
||||
mBuilder.setDataCallback(callback);
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
|
||||
AudioStreamDataCallback *callback2 = mStream->getDataCallback();
|
||||
ASSERT_EQ(callback, callback2);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetUsageReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
Usage u = mStream->getUsage();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getUsage(), u);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetContentTypeReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
ContentType c = mStream->getContentType();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getContentType(), c);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetInputPresetReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
auto i = mStream->getInputPreset();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getInputPreset(), i);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetSessionIdReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
auto s = mStream->getSessionId();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getSessionId(), s);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, StreamStateIsClosed){
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
ASSERT_EQ(mStream->getState(), StreamState::Closed);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetXRunCountReturnsLastKnownValue){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
if (mStream->isXRunCountSupported()){
|
||||
auto i = mStream->getXRunCount();
|
||||
ASSERT_EQ(mStream->getXRunCount(), i);
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetFramesPerBurstReturnsLastKnownValue){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
auto f = mStream->getFramesPerBurst();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getFramesPerBurst(), f);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetBytesPerFrameReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
auto f = mStream->getBytesPerFrame();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getBytesPerFrame(), f);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetBytesPerSampleReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
auto f = mStream->getBytesPerSample();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getBytesPerSample(), f);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetFramesWrittenReturnsLastKnownValue){
|
||||
mBuilder.setFormat(AudioFormat::I16);
|
||||
mBuilder.setChannelCount(1);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->setBufferSizeInFrames(mStream->getBufferCapacityInFrames()), Result::OK);
|
||||
mStream->start();
|
||||
|
||||
int16_t buffer[4] = { 1, 2, 3, 4 };
|
||||
Result r = mStream->write(&buffer, 4, 0);
|
||||
if (r != Result::OK) {
|
||||
FAIL() << "Could not write to audio stream";
|
||||
}
|
||||
|
||||
auto f = mStream->getFramesWritten();
|
||||
ASSERT_EQ(f, 4);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getFramesWritten(), f);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetFramesReadReturnsLastKnownValue) {
|
||||
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setFormat(AudioFormat::I16);
|
||||
mBuilder.setChannelCount(1);
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
mStream->start();
|
||||
|
||||
int16_t buffer[192];
|
||||
auto r = mStream->read(&buffer, 192, 1000 * kNanosPerMillisecond);
|
||||
ASSERT_EQ(r.value(), 192);
|
||||
|
||||
auto f = mStream->getFramesRead();
|
||||
ASSERT_EQ(f, 192);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getFramesRead(), f);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetTimestampReturnsErrorClosedIfSupported){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
|
||||
int64_t framePosition;
|
||||
int64_t presentationTime;
|
||||
|
||||
auto r = mStream->getTimestamp(CLOCK_MONOTONIC, &framePosition, &presentationTime);
|
||||
bool isTimestampSupported = (r == Result::OK);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
|
||||
if (isTimestampSupported){
|
||||
ASSERT_EQ(mStream->getTimestamp(CLOCK_MONOTONIC, &framePosition, &presentationTime), Result::ErrorClosed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetAudioApiReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
AudioApi a = mStream->getAudioApi();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->getAudioApi(), a);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, GetUsesAAudioReturnsLastKnownValue){
|
||||
ASSERT_TRUE(openStream());
|
||||
bool a = mStream->usesAAudio();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_EQ(mStream->usesAAudio(), a);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, StreamStateControlsReturnClosed){
|
||||
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
Result r = mStream->close();
|
||||
EXPECT_EQ(r, Result::ErrorClosed) << convertToText(r);
|
||||
r = mStream->start();
|
||||
EXPECT_EQ(r, Result::ErrorClosed) << convertToText(r);
|
||||
EXPECT_EQ(mStream->pause(), Result::ErrorClosed);
|
||||
EXPECT_EQ(mStream->flush(), Result::ErrorClosed);
|
||||
EXPECT_EQ(mStream->stop(), Result::ErrorClosed);
|
||||
EXPECT_EQ(mStream->requestStart(), Result::ErrorClosed);
|
||||
EXPECT_EQ(mStream->requestPause(), Result::ErrorClosed);
|
||||
EXPECT_EQ(mStream->requestFlush(), Result::ErrorClosed);
|
||||
EXPECT_EQ(mStream->requestStop(), Result::ErrorClosed);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, WaitForStateChangeReturnsClosed){
|
||||
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
StreamState next;
|
||||
Result r = mStream->waitForStateChange(StreamState::Open, &next, 0);
|
||||
EXPECT_TRUE(r == Result::OK || r == Result::ErrorClosed) << convertToText(r);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, SetBufferSizeInFramesReturnsClosed){
|
||||
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
auto r = mStream->setBufferSizeInFrames(192);
|
||||
ASSERT_EQ(r.error(), Result::ErrorClosed);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, CalculateLatencyInMillisReturnsClosedIfSupported){
|
||||
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
|
||||
if (mStream->getAudioApi() == AudioApi::AAudio){
|
||||
auto r = mStream->calculateLatencyMillis();
|
||||
ASSERT_EQ(r.error(), Result::ErrorInvalidState);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, ReadReturnsClosed){
|
||||
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
|
||||
int buffer[8]{0};
|
||||
auto r = mStream->read(buffer, 1, 0);
|
||||
ASSERT_EQ(r.error(), Result::ErrorClosed);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, WriteReturnsClosed){
|
||||
|
||||
ASSERT_TRUE(openAndCloseStream());
|
||||
|
||||
int buffer[8]{0};
|
||||
auto r = mStream->write(buffer, 1, 0);
|
||||
ASSERT_EQ(r.error(), Result::ErrorClosed);
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, DelayBeforeCloseInput){
|
||||
if (AudioStreamBuilder::isAAudioRecommended()) {
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
testDelayBeforeClose();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, DelayBeforeCloseOutput){
|
||||
if (AudioStreamBuilder::isAAudioRecommended()) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
testDelayBeforeClose();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, DelayBeforeCloseInputOpenSL){
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
testDelayBeforeClose();
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, DelayBeforeCloseOutputOpenSL){
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
testDelayBeforeClose();
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, TestReleaseInput){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_TRUE(releaseStream());
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, TestReleaseInputOpenSLES){
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_TRUE(releaseStream());
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, TestReleaseOutput){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_TRUE(releaseStream());
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamClosedReturnValues, TestReleaseOutputOpenSLES){
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_TRUE(releaseStream());
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
99
externals/oboe/tests/testStreamFramesProcessed.cpp
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
class FramesProcessedCallback : public AudioStreamDataCallback {
|
||||
public:
|
||||
DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
};
|
||||
|
||||
using StreamFramesProcessedParams = std::tuple<Direction, int32_t, bool>;
|
||||
|
||||
class StreamFramesProcessed : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<StreamFramesProcessedParams> {
|
||||
|
||||
protected:
|
||||
void TearDown() override;
|
||||
|
||||
static constexpr int PROCESS_TIME_SECONDS = 5;
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
};
|
||||
|
||||
void StreamFramesProcessed::TearDown() {
|
||||
if (mStream) {
|
||||
mStream->close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StreamFramesProcessed, VerifyFramesProcessed) {
|
||||
const Direction direction = std::get<0>(GetParam());
|
||||
const int32_t sampleRate = std::get<1>(GetParam());
|
||||
const bool useOboeSampleRateConversion = std::get<2>(GetParam());
|
||||
|
||||
SampleRateConversionQuality srcQuality = useOboeSampleRateConversion ?
|
||||
SampleRateConversionQuality::Medium : SampleRateConversionQuality::None;
|
||||
|
||||
AudioStreamDataCallback *callback = new FramesProcessedCallback();
|
||||
mBuilder.setDirection(direction)
|
||||
->setFormat(AudioFormat::I16)
|
||||
->setSampleRate(sampleRate)
|
||||
->setSampleRateConversionQuality(srcQuality)
|
||||
->setPerformanceMode(PerformanceMode::LowLatency)
|
||||
->setSharingMode(SharingMode::Exclusive)
|
||||
->setDataCallback(callback);
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to open stream." << convertToText(r);
|
||||
|
||||
r = mStream->start();
|
||||
ASSERT_EQ(r, Result::OK) << "Failed to start stream." << convertToText(r);
|
||||
sleep(PROCESS_TIME_SECONDS);
|
||||
|
||||
// The frames written should be close to sampleRate * PROCESS_TIME_SECONDS
|
||||
const int kDeltaFramesWindowInFrames = 30000;
|
||||
const int64_t framesWritten = mStream->getFramesWritten();
|
||||
const int64_t framesRead = mStream->getFramesRead();
|
||||
EXPECT_NEAR(framesWritten, sampleRate * PROCESS_TIME_SECONDS, kDeltaFramesWindowInFrames);
|
||||
EXPECT_NEAR(framesRead, sampleRate * PROCESS_TIME_SECONDS, kDeltaFramesWindowInFrames);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
StreamFramesProcessedTest,
|
||||
StreamFramesProcessed,
|
||||
::testing::Values(
|
||||
StreamFramesProcessedParams({Direction::Output, 8000, true}),
|
||||
StreamFramesProcessedParams({Direction::Output, 44100, true}),
|
||||
StreamFramesProcessedParams({Direction::Output, 96000, true}),
|
||||
StreamFramesProcessedParams({Direction::Input, 8000, true}),
|
||||
StreamFramesProcessedParams({Direction::Input, 44100, true}),
|
||||
StreamFramesProcessedParams({Direction::Input, 96000, true}),
|
||||
StreamFramesProcessedParams({Direction::Output, 8000, false}),
|
||||
StreamFramesProcessedParams({Direction::Output, 44100, false}),
|
||||
StreamFramesProcessedParams({Direction::Output, 96000, false}),
|
||||
StreamFramesProcessedParams({Direction::Input, 8000, false}),
|
||||
StreamFramesProcessedParams({Direction::Input, 44100, false}),
|
||||
StreamFramesProcessedParams({Direction::Input, 96000, false})
|
||||
)
|
||||
);
|
746
externals/oboe/tests/testStreamOpen.cpp
vendored
Normal file
|
@ -0,0 +1,746 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <aaudio/AAudioExtensions.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
#include <android/api-level.h>
|
||||
#ifndef __ANDROID_API_S__
|
||||
#define __ANDROID_API_S__ 31
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_S_V2__
|
||||
#define __ANDROID_API_S_V2__ 32
|
||||
#endif
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
class CallbackSizeMonitor : public AudioStreamCallback {
|
||||
public:
|
||||
DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
|
||||
framesPerCallback = numFrames;
|
||||
callbackCount++;
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
// This is exposed publicly so that the number of frames per callback can be tested.
|
||||
std::atomic<int32_t> framesPerCallback{0};
|
||||
std::atomic<int32_t> callbackCount{0};
|
||||
};
|
||||
|
||||
class StreamOpen : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
|
||||
bool openStream() {
|
||||
EXPECT_EQ(mStream, nullptr);
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
EXPECT_EQ(0, openCount) << "Should start with a fresh object every time.";
|
||||
openCount++;
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
bool closeStream() {
|
||||
if (mStream){
|
||||
Result r = mStream->close();
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to close stream. " << convertToText(r);
|
||||
usleep(500 * 1000); // give previous stream time to settle
|
||||
return (r == Result::OK);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void checkSampleRateConversionAdvancing(Direction direction) {
|
||||
CallbackSizeMonitor callback;
|
||||
|
||||
mBuilder.setDirection(direction);
|
||||
if (mBuilder.isAAudioRecommended()) {
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
}
|
||||
mBuilder.setCallback(&callback);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
mBuilder.setSampleRate(44100);
|
||||
mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium);
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
int timeout = 20;
|
||||
while (callback.framesPerCallback == 0 && timeout > 0) {
|
||||
usleep(50 * 1000);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
// Catch Issue #1166
|
||||
mStream->getTimestamp(CLOCK_MONOTONIC); // should not crash
|
||||
mStream->getTimestamp(CLOCK_MONOTONIC, nullptr, nullptr); // should not crash
|
||||
|
||||
ASSERT_GT(callback.callbackCount, 0);
|
||||
ASSERT_GT(callback.framesPerCallback, 0);
|
||||
ASSERT_EQ(mStream->requestStop(), Result::OK);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
int32_t openCount = 0;
|
||||
|
||||
};
|
||||
|
||||
class StreamOpenOutput : public StreamOpen {};
|
||||
class StreamOpenInput : public StreamOpen {};
|
||||
|
||||
TEST_F(StreamOpenOutput, ForOpenSLESDefaultSampleRateIsUsed){
|
||||
|
||||
DefaultStreamValues::SampleRate = 44100;
|
||||
DefaultStreamValues::FramesPerBurst = 192;
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getSampleRate(), 44100);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, ForOpenSLESDefaultFramesPerBurstIsUsed){
|
||||
|
||||
DefaultStreamValues::SampleRate = 48000;
|
||||
DefaultStreamValues::FramesPerBurst = 128; // used for low latency
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
ASSERT_TRUE(openStream());
|
||||
// Some devices like emulators may not support Low Latency
|
||||
if (mStream->getPerformanceMode() == PerformanceMode::LowLatency) {
|
||||
ASSERT_EQ(mStream->getFramesPerBurst(), 128);
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, ForOpenSLESDefaultChannelCountIsUsed){
|
||||
|
||||
DefaultStreamValues::ChannelCount = 1;
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getChannelCount(), 1);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, OutputForOpenSLESPerformanceModeShouldBeNone){
|
||||
// We will not get a LowLatency stream if we request 16000 Hz.
|
||||
mBuilder.setSampleRate(16000);
|
||||
mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::None);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, InputForOpenSLESPerformanceModeShouldBeNone){
|
||||
// We will not get a LowLatency stream if we request 16000 Hz.
|
||||
mBuilder.setSampleRate(16000);
|
||||
mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::None);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, ForOpenSlesIllegalFormatRejectedOutput) {
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
mBuilder.setFormat(static_cast<AudioFormat>(666));
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r);
|
||||
if (mStream != nullptr) {
|
||||
mStream->close(); // just in case it accidentally opened
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, ForOpenSlesIllegalFormatRejectedInput) {
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setFormat(static_cast<AudioFormat>(666));
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r);
|
||||
if (mStream != nullptr) {
|
||||
mStream->close(); // just in case it accidentally opened
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the callback is called with the requested FramesPerCallback
|
||||
TEST_F(StreamOpenOutput, OpenSLESFramesPerCallback) {
|
||||
const int kRequestedFramesPerCallback = 417;
|
||||
CallbackSizeMonitor callback;
|
||||
|
||||
DefaultStreamValues::SampleRate = 48000;
|
||||
DefaultStreamValues::ChannelCount = 2;
|
||||
DefaultStreamValues::FramesPerBurst = 192;
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
|
||||
mBuilder.setCallback(&callback);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
int timeout = 20;
|
||||
while (callback.framesPerCallback == 0 && timeout > 0) {
|
||||
usleep(50 * 1000);
|
||||
timeout--;
|
||||
}
|
||||
ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
|
||||
ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
|
||||
ASSERT_EQ(mStream->requestStop(), Result::OK);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
// Make sure the LowLatency callback has the requested FramesPerCallback.
|
||||
TEST_F(StreamOpen, AAudioFramesPerCallbackLowLatency) {
|
||||
const int kRequestedFramesPerCallback = 192;
|
||||
CallbackSizeMonitor callback;
|
||||
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
|
||||
mBuilder.setCallback(&callback);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
int timeout = 20;
|
||||
while (callback.framesPerCallback == 0 && timeout > 0) {
|
||||
usleep(50 * 1000);
|
||||
timeout--;
|
||||
}
|
||||
ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
|
||||
ASSERT_EQ(mStream->requestStop(), Result::OK);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
// Make sure the regular callback has the requested FramesPerCallback.
|
||||
TEST_F(StreamOpen, AAudioFramesPerCallbackNone) {
|
||||
const int kRequestedFramesPerCallback = 1024;
|
||||
CallbackSizeMonitor callback;
|
||||
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
|
||||
mBuilder.setCallback(&callback);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::None);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
int timeout = 20;
|
||||
while (callback.framesPerCallback == 0 && timeout > 0) {
|
||||
usleep(50 * 1000);
|
||||
timeout--;
|
||||
}
|
||||
ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
|
||||
ASSERT_EQ(mStream->requestStop(), Result::OK);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, RecordingFormatUnspecifiedReturnsI16BeforeMarshmallow){
|
||||
|
||||
if (getSdkVersion() < __ANDROID_API_M__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setFormat(AudioFormat::Unspecified);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, RecordingFormatUnspecifiedReturnsFloatOnMarshmallowAndLater){
|
||||
|
||||
if (getSdkVersion() >= __ANDROID_API_M__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setFormat(AudioFormat::Unspecified);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, RecordingFormatFloatReturnsErrorBeforeMarshmallow){
|
||||
|
||||
if (getSdkVersion() < __ANDROID_API_M__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setFormat(AudioFormat::Float);
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
ASSERT_EQ(r, Result::ErrorInvalidFormat) << convertToText(r);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, RecordingFormatFloatReturnsFloatOnMarshmallowAndLater){
|
||||
|
||||
if (getSdkVersion() >= __ANDROID_API_M__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setFormat(AudioFormat::Float);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, RecordingFormatI16ReturnsI16){
|
||||
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setFormat(AudioFormat::I16);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, PlaybackFormatUnspecifiedReturnsI16BeforeLollipop){
|
||||
|
||||
if (getSdkVersion() < __ANDROID_API_L__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setFormat(AudioFormat::Unspecified);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, PlaybackFormatUnspecifiedReturnsFloatOnLollipopAndLater){
|
||||
|
||||
if (getSdkVersion() >= __ANDROID_API_L__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setFormat(AudioFormat::Unspecified);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, PlaybackFormatFloatReturnsErrorBeforeLollipop){
|
||||
|
||||
if (getSdkVersion() < __ANDROID_API_L__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setFormat(AudioFormat::Float);
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
ASSERT_EQ(r, Result::ErrorInvalidFormat);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, PlaybackFormatFloatReturnsFloatWithFormatConversionAllowed){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setFormat(AudioFormat::Float);
|
||||
mBuilder.setFormatConversionAllowed(true);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, PlaybackFormatFloatReturnsFloatOnLollipopAndLater){
|
||||
|
||||
if (getSdkVersion() >= __ANDROID_API_L__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setFormat(AudioFormat::Float);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, PlaybackFormatI16ReturnsI16) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setFormat(AudioFormat::I16);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, OpenCloseLowLatencyStream){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
float *buf = new float[100];
|
||||
ASSERT_TRUE(openStream());
|
||||
delete[] buf;
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, LowLatencyStreamHasSmallBufferSize){
|
||||
|
||||
if (mBuilder.isAAudioRecommended()) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
ASSERT_TRUE(openStream());
|
||||
int32_t bufferSize = mStream->getBufferSizeInFrames();
|
||||
int32_t burst = mStream->getFramesPerBurst();
|
||||
ASSERT_TRUE(closeStream());
|
||||
ASSERT_LE(bufferSize, burst * 3);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the parameters get copied from the child stream.
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSampleRate44100FilterConfiguration) {
|
||||
if (mBuilder.isAAudioRecommended()) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
mBuilder.setSharingMode(SharingMode::Exclusive);
|
||||
// Try to force the use of a FilterAudioStream by requesting conversion.
|
||||
mBuilder.setSampleRate(44100);
|
||||
mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium);
|
||||
ASSERT_TRUE(openStream());
|
||||
if (getSdkVersion() >= __ANDROID_API_U__) {
|
||||
ASSERT_LT(0, mStream->getHardwareSampleRate());
|
||||
ASSERT_LT(0, mStream->getHardwareChannelCount());
|
||||
ASSERT_LT(0, (int)mStream->getHardwareFormat());
|
||||
}
|
||||
// If MMAP is not supported then we cannot get an EXCLUSIVE mode stream.
|
||||
if (!AAudioExtensions::getInstance().isMMapSupported()) {
|
||||
ASSERT_NE(SharingMode::Exclusive, mStream->getSharingMode()); // IMPOSSIBLE
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
// See if sample rate conversion by Oboe is calling the callback.
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSampleRate44100) {
|
||||
checkSampleRateConversionAdvancing(Direction::Output);
|
||||
}
|
||||
|
||||
// See if sample rate conversion by Oboe is calling the callback.
|
||||
TEST_F(StreamOpenInput, AAudioInputSampleRate44100) {
|
||||
checkSampleRateConversionAdvancing(Direction::Input);
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetPackageName){
|
||||
if (getSdkVersion() >= __ANDROID_API_S__){
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setPackageName("com.google.oboe.tests.unittestrunner");
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetPackageName){
|
||||
if (getSdkVersion() >= __ANDROID_API_S__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setPackageName("com.google.oboe.tests.unittestrunner");
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetAttributionTag){
|
||||
if (getSdkVersion() >= __ANDROID_API_S__){
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setAttributionTag("TestSetOutputAttributionTag");
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetAttributionTag){
|
||||
if (getSdkVersion() >= __ANDROID_API_S__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setAttributionTag("TestSetInputAttributionTag");
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->requestStart(), Result::OK);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetSpatializationBehavior) {
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setSpatializationBehavior(SpatializationBehavior::Auto);
|
||||
ASSERT_TRUE(openStream());
|
||||
if (getSdkVersion() >= __ANDROID_API_S_V2__){
|
||||
ASSERT_EQ(mStream->getSpatializationBehavior(), SpatializationBehavior::Auto);
|
||||
} else {
|
||||
ASSERT_EQ(mStream->getSpatializationBehavior(), SpatializationBehavior::Never);
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetSpatializationBehavior) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setSpatializationBehavior(SpatializationBehavior::Never);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getSpatializationBehavior(), SpatializationBehavior::Never);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, OpenSLESOutputSetSpatializationBehavior) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setSpatializationBehavior(SpatializationBehavior::Auto);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getSpatializationBehavior(), SpatializationBehavior::Never);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetSpatializationBehaviorUnspecified) {
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setSpatializationBehavior(SpatializationBehavior::Unspecified);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getSpatializationBehavior(), SpatializationBehavior::Never);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetSpatializationBehaviorUnspecified) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setSpatializationBehavior(SpatializationBehavior::Unspecified);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getSpatializationBehavior(), SpatializationBehavior::Never);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetIsContentSpatialized) {
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setIsContentSpatialized(true);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->isContentSpatialized(), true);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetIsContentSpatialized) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setIsContentSpatialized(true);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->isContentSpatialized(), true);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, OpenSLESOutputSetIsContentSpatialized) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setIsContentSpatialized(true);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->isContentSpatialized(), true);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetIsContentSpatializedFalse) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setIsContentSpatialized(false);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->isContentSpatialized(), false);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetIsContentSpatializedUnspecified) {
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->isContentSpatialized(), false);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetIsContentSpatializedUnspecified) {
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->isContentSpatialized(), false);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, OutputForOpenSLESPerformanceModeNoneGetBufferSizeInFrames){
|
||||
mBuilder.setPerformanceMode(PerformanceMode::None);
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream());
|
||||
EXPECT_GT(mStream->getBufferSizeInFrames(), 0);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, OboeExtensions){
|
||||
if (OboeExtensions::isMMapSupported()) {
|
||||
ASSERT_EQ(OboeExtensions::setMMapEnabled(true), 0);
|
||||
ASSERT_TRUE(OboeExtensions::isMMapEnabled());
|
||||
|
||||
ASSERT_EQ(OboeExtensions::setMMapEnabled(false), 0);
|
||||
ASSERT_FALSE(OboeExtensions::isMMapEnabled());
|
||||
ASSERT_TRUE(openStream());
|
||||
EXPECT_FALSE(OboeExtensions::isMMapUsed(mStream.get()));
|
||||
ASSERT_TRUE(closeStream());
|
||||
|
||||
ASSERT_EQ(OboeExtensions::setMMapEnabled(true), 0);
|
||||
ASSERT_TRUE(OboeExtensions::isMMapEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeUnspecifiedUnprocessed){
|
||||
if (getSdkVersion() >= __ANDROID_API_R__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setInputPreset(InputPreset::Unprocessed);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Disabled);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeUnspecifiedVoiceCommunication){
|
||||
if (getSdkVersion() >= __ANDROID_API_R__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setInputPreset(InputPreset::VoiceCommunication);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Enabled);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeVoiceDisabled){
|
||||
if (getSdkVersion() >= __ANDROID_API_R__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setInputPreset(InputPreset::VoiceCommunication);
|
||||
mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Disabled);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Disabled);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetPrivacySensitiveModeUnprocessedEnabled){
|
||||
if (getSdkVersion() >= __ANDROID_API_R__){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setInputPreset(InputPreset::Unprocessed);
|
||||
mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Enabled);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetPrivacySensitiveModeGetsUnspecified){
|
||||
if (getSdkVersion() >= __ANDROID_API_R__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Unspecified);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, OpenSLESInputSetPrivacySensitiveModeDoesNotCrash){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setInputPreset(InputPreset::Unprocessed);
|
||||
mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Unspecified);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, OldAndroidVersionInputSetPrivacySensitiveModeDoesNotCrash){
|
||||
if (getSdkVersion() < __ANDROID_API_R__) {
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setInputPreset(InputPreset::Unprocessed);
|
||||
mBuilder.setPrivacySensitiveMode(PrivacySensitiveMode::Enabled);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getPrivacySensitiveMode(), PrivacySensitiveMode::Unspecified);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetAllowedCapturePolicyUnspecifiedGetsAll){
|
||||
if (getSdkVersion() >= __ANDROID_API_Q__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setAllowedCapturePolicy(AllowedCapturePolicy::Unspecified);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::All);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetAllowedCapturePolicyAll){
|
||||
if (getSdkVersion() >= __ANDROID_API_Q__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setAllowedCapturePolicy(AllowedCapturePolicy::All);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::All);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetAllowedCapturePolicySystem){
|
||||
if (getSdkVersion() >= __ANDROID_API_Q__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setAllowedCapturePolicy(AllowedCapturePolicy::System);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::System);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputSetAllowedCapturePolicyNone){
|
||||
if (getSdkVersion() >= __ANDROID_API_Q__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setAllowedCapturePolicy(AllowedCapturePolicy::None);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::None);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioOutputDoNotSetAllowedCapturePolicy){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
ASSERT_TRUE(openStream());
|
||||
if (getSdkVersion() >= __ANDROID_API_Q__){
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::All);
|
||||
} else {
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::Unspecified);
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, OpenSLESOutputSetAllowedCapturePolicyAllGetsUnspecified){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::OpenSLES);
|
||||
mBuilder.setAllowedCapturePolicy(AllowedCapturePolicy::All);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::Unspecified);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenOutput, AAudioBeforeQOutputSetAllowedCapturePolicyAllGetsUnspecified){
|
||||
if (getSdkVersion() < __ANDROID_API_Q__){
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
mBuilder.setAudioApi(AudioApi::AAudio);
|
||||
mBuilder.setAllowedCapturePolicy(AllowedCapturePolicy::All);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::Unspecified);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(StreamOpenInput, AAudioInputSetAllowedCapturePolicyAllGetsUnspecified){
|
||||
mBuilder.setDirection(Direction::Input);
|
||||
mBuilder.setAllowedCapturePolicy(AllowedCapturePolicy::All);
|
||||
ASSERT_TRUE(openStream());
|
||||
ASSERT_EQ(mStream->getAllowedCapturePolicy(), AllowedCapturePolicy::Unspecified);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
305
externals/oboe/tests/testStreamStates.cpp
vendored
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
// Sleep between close and open to avoid a race condition inside Android Audio.
|
||||
// On a Pixel 2 emulator on a fast Linux host, the minimum value is around 16 msec.
|
||||
constexpr int kOboeOpenCloseSleepMSec = 100;
|
||||
|
||||
class StreamStates : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
|
||||
void SetUp(){
|
||||
mBuilder.setPerformanceMode(PerformanceMode::None);
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
}
|
||||
|
||||
bool openStream(Direction direction) {
|
||||
usleep(100 * 1000);
|
||||
mBuilder.setDirection(direction);
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
if (r != Result::OK)
|
||||
return false;
|
||||
|
||||
Direction d = mStream->getDirection();
|
||||
EXPECT_EQ(d, direction) << convertToText(mStream->getDirection());
|
||||
return (d == direction);
|
||||
}
|
||||
|
||||
bool openStream() {
|
||||
return openStream(Direction::Output);
|
||||
}
|
||||
|
||||
bool openInputStream() {
|
||||
return openStream(Direction::Input);
|
||||
}
|
||||
|
||||
bool closeStream() {
|
||||
Result r = mStream->close();
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to close stream. " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
void checkStreamStateIsStartedAfterStartingTwice(Direction direction) {
|
||||
ASSERT_TRUE(openStream(direction));
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK) << "requestStart returned: " << convertToText(r);
|
||||
r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
EXPECT_EQ(next, StreamState::Started);
|
||||
|
||||
next = StreamState::Unknown;
|
||||
r = mStream->requestStart();
|
||||
// TODO On P, AAudio is returning ErrorInvalidState for Output and OK for Input
|
||||
// EXPECT_EQ(r, Result::OK) << "requestStart returned: " << convertToText(r);
|
||||
r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
ASSERT_EQ(next, StreamState::Started);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
void checkStreamStateIsStoppedAfterStoppingTwice(Direction direction) {
|
||||
ASSERT_TRUE(openStream(direction));
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
r = mStream->requestStop();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
EXPECT_EQ(next, StreamState::Stopped);
|
||||
|
||||
r = mStream->requestStop();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
next = StreamState::Unknown;
|
||||
r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
ASSERT_EQ(next, StreamState::Stopped);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
// TODO: This seems to fail intermittently on Pixel OC_MR1 !
|
||||
void checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction direction) {
|
||||
ASSERT_TRUE(openStream(direction));
|
||||
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
// It should be safe to close without stopping.
|
||||
// The underlying API should stop the stream.
|
||||
ASSERT_TRUE(closeStream());
|
||||
|
||||
usleep(kOboeOpenCloseSleepMSec * 1000); // avoid race condition in emulator
|
||||
|
||||
ASSERT_TRUE(openInputStream());
|
||||
r = mStream->requestStart();
|
||||
ASSERT_EQ(r, Result::OK) << "requestStart returned: " << convertToText(r);
|
||||
|
||||
r = mStream->requestStop();
|
||||
EXPECT_EQ(r, Result::OK) << "requestStop returned: " << convertToText(r);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
static constexpr int kTimeoutInNanos = 500 * kNanosPerMillisecond;
|
||||
|
||||
};
|
||||
|
||||
TEST_F(StreamStates, OutputStreamStateIsOpenAfterOpening){
|
||||
ASSERT_TRUE(openStream());
|
||||
StreamState next = StreamState::Unknown;
|
||||
Result r = mStream->waitForStateChange(StreamState::Uninitialized, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK) << convertToText(r);
|
||||
ASSERT_EQ(next, StreamState::Open) << convertToText(next);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputStreamStateIsStartedAfterStarting){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
ASSERT_EQ(next, StreamState::Started);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputStreamStateIsPausedAfterPausing){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
r = mStream->requestPause();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
r = mStream->waitForStateChange(StreamState::Pausing, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
ASSERT_EQ(next, StreamState::Paused);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputStreamStateIsStoppedAfterStopping){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
r = mStream->requestStop();
|
||||
r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
ASSERT_EQ(next, StreamState::Stopped);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputStreamStateIsOpenAfterOpening){
|
||||
ASSERT_TRUE(openInputStream());
|
||||
StreamState next = StreamState::Unknown;
|
||||
Result r = mStream->waitForStateChange(StreamState::Uninitialized, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK) << convertToText(r);
|
||||
ASSERT_EQ(next, StreamState::Open) << convertToText(next);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputStreamStateIsStartedAfterStarting){
|
||||
|
||||
ASSERT_TRUE(openInputStream());
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
ASSERT_EQ(next, StreamState::Started);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputStreamStateIsStartedAfterStartingTwice){
|
||||
checkStreamStateIsStartedAfterStartingTwice(Direction::Output);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputStreamStateIsStartedAfterStartingTwice){
|
||||
checkStreamStateIsStartedAfterStartingTwice(Direction::Input);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputStreamStateIsStoppedAfterStoppingTwice){
|
||||
checkStreamStateIsStoppedAfterStoppingTwice(Direction::Output);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputStreamStateIsStoppedAfterStoppingTwice){
|
||||
checkStreamStateIsStoppedAfterStoppingTwice(Direction::Input);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputStreamStateIsPausedAfterPausingTwice){
|
||||
ASSERT_TRUE(openStream());
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
|
||||
r = mStream->requestPause();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
r = mStream->waitForStateChange(StreamState::Pausing, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
EXPECT_EQ(next, StreamState::Paused);
|
||||
|
||||
// requestPause() while already paused could leave us in Pausing in AAudio O_MR1.
|
||||
r = mStream->requestPause();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
next = StreamState::Unknown;
|
||||
r = mStream->waitForStateChange(StreamState::Pausing, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
ASSERT_EQ(next, StreamState::Paused);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputStreamDoesNotSupportPause){
|
||||
|
||||
ASSERT_TRUE(openInputStream());
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
r = mStream->requestPause();
|
||||
|
||||
ASSERT_EQ(r, Result::ErrorUnimplemented) << convertToText(r);
|
||||
mStream->requestStop();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputStreamLeftRunningShouldNotInterfereWithNextOpen) {
|
||||
checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Output);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputStreamLeftRunningShouldNotInterfereWithNextOpen) {
|
||||
checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Input);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, OutputLowLatencyStreamLeftRunningShouldNotInterfereWithNextOpen) {
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Output);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputLowLatencyStreamLeftRunningShouldNotInterfereWithNextOpen) {
|
||||
mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Input);
|
||||
}
|
||||
|
||||
TEST_F(StreamStates, InputStreamStateIsStoppedAfterStopping){
|
||||
|
||||
ASSERT_TRUE(openInputStream());
|
||||
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK) << "requestStart returned: " << convertToText(r);
|
||||
|
||||
r = mStream->requestStop();
|
||||
EXPECT_EQ(r, Result::OK) << "requestStop returned: " << convertToText(r);
|
||||
|
||||
r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK) << "waitForStateChange returned: " << convertToText(r);
|
||||
|
||||
ASSERT_EQ(next, StreamState::Stopped);
|
||||
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
130
externals/oboe/tests/testStreamStop.cpp
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
using TestStreamStopParams = std::tuple<Direction, AudioApi, PerformanceMode>;
|
||||
|
||||
class TestStreamStop : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<TestStreamStopParams> {
|
||||
|
||||
protected:
|
||||
|
||||
void SetUp(){
|
||||
mBuilder.setPerformanceMode(PerformanceMode::None);
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
}
|
||||
|
||||
bool openStream(Direction direction, AudioApi audioApi, PerformanceMode perfMode) {
|
||||
mBuilder.setDirection(direction);
|
||||
if (mBuilder.isAAudioRecommended()) {
|
||||
mBuilder.setAudioApi(audioApi);
|
||||
}
|
||||
mBuilder.setPerformanceMode(perfMode);
|
||||
mBuilder.setChannelCount(1);
|
||||
mBuilder.setFormat(AudioFormat::I16);
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
if (r != Result::OK)
|
||||
return false;
|
||||
|
||||
Direction d = mStream->getDirection();
|
||||
EXPECT_EQ(d, direction) << convertToText(mStream->getDirection());
|
||||
return (d == direction);
|
||||
}
|
||||
|
||||
bool openStream(AudioStreamBuilder &builder) {
|
||||
Result r = builder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
void stopWhileUsingLargeBuffer() {
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
EXPECT_EQ(next, StreamState::Started) << "next = " << convertToText(next);
|
||||
|
||||
std::shared_ptr<AudioStream> str = mStream;
|
||||
|
||||
int16_t buffer[kFramesToWrite] = {};
|
||||
|
||||
std::thread stopper([str] {
|
||||
int64_t estimatedCompletionTimeUs = kMicroSecondsPerSecond * kFramesToWrite / str->getSampleRate();
|
||||
usleep(estimatedCompletionTimeUs / 2); // Stop halfway during the read/write
|
||||
EXPECT_EQ(str->close(), Result::OK);
|
||||
});
|
||||
|
||||
if (mBuilder.getDirection() == Direction::Output) {
|
||||
r = mStream->write(&buffer, kFramesToWrite, kTimeoutInNanos);
|
||||
} else {
|
||||
r = mStream->read(&buffer, kFramesToWrite, kTimeoutInNanos);
|
||||
}
|
||||
if (r != Result::OK) {
|
||||
FAIL() << "Could not read/write to audio stream: " << static_cast<int>(r);
|
||||
}
|
||||
|
||||
stopper.join();
|
||||
r = mStream->waitForStateChange(StreamState::Started, &next,
|
||||
1000 * kNanosPerMillisecond);
|
||||
if ((r != Result::ErrorClosed) && (r != Result::OK)) {
|
||||
FAIL() << "Wrong closed result type: " << static_cast<int>(r);
|
||||
}
|
||||
}
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
static constexpr int kTimeoutInNanos = 1000 * kNanosPerMillisecond;
|
||||
static constexpr int64_t kMicroSecondsPerSecond = 1000000;
|
||||
static constexpr int kFramesToWrite = 10000;
|
||||
|
||||
};
|
||||
|
||||
TEST_P(TestStreamStop, VerifyTestStreamStop) {
|
||||
const Direction direction = std::get<0>(GetParam());
|
||||
const AudioApi audioApi = std::get<1>(GetParam());
|
||||
const PerformanceMode performanceMode = std::get<2>(GetParam());
|
||||
|
||||
ASSERT_TRUE(openStream(direction, audioApi, performanceMode));
|
||||
stopWhileUsingLargeBuffer();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestStreamStopTest,
|
||||
TestStreamStop,
|
||||
::testing::Values(
|
||||
TestStreamStopParams({Direction::Output, AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestStreamStopParams({Direction::Output, AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestStreamStopParams({Direction::Output, AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestStreamStopParams({Direction::Output, AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestStreamStopParams({Direction::Output, AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestStreamStopParams({Direction::Output, AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
|
||||
TestStreamStopParams({Direction::Input, AudioApi::AAudio, PerformanceMode::LowLatency}),
|
||||
TestStreamStopParams({Direction::Input, AudioApi::AAudio, PerformanceMode::None}),
|
||||
TestStreamStopParams({Direction::Input, AudioApi::AAudio, PerformanceMode::PowerSaving}),
|
||||
TestStreamStopParams({Direction::Input, AudioApi::OpenSLES, PerformanceMode::LowLatency}),
|
||||
TestStreamStopParams({Direction::Input, AudioApi::OpenSLES, PerformanceMode::None}),
|
||||
TestStreamStopParams({Direction::Input, AudioApi::OpenSLES, PerformanceMode::PowerSaving})
|
||||
)
|
||||
);
|
251
externals/oboe/tests/testStreamWaitState.cpp
vendored
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
class TestStreamWaitState : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
|
||||
void SetUp(){
|
||||
mBuilder.setPerformanceMode(PerformanceMode::None);
|
||||
mBuilder.setDirection(Direction::Output);
|
||||
}
|
||||
|
||||
bool openStream(Direction direction, PerformanceMode perfMode) {
|
||||
mBuilder.setDirection(direction);
|
||||
mBuilder.setPerformanceMode(perfMode);
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
if (r != Result::OK)
|
||||
return false;
|
||||
|
||||
Direction d = mStream->getDirection();
|
||||
EXPECT_EQ(d, direction) << convertToText(mStream->getDirection());
|
||||
return (d == direction);
|
||||
}
|
||||
|
||||
bool openStream(AudioStreamBuilder &builder) {
|
||||
Result r = builder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
bool closeStream() {
|
||||
Result r = mStream->close();
|
||||
EXPECT_TRUE(r == Result::OK || r == Result::ErrorClosed) <<
|
||||
"Failed to close stream. " << convertToText(r);
|
||||
return (r == Result::OK || r == Result::ErrorClosed);
|
||||
}
|
||||
|
||||
// if zero then don't wait for a state change
|
||||
void checkWaitForStateChangeTimeout(int64_t timeout = kTimeoutInNanos) {
|
||||
StreamState next = StreamState::Unknown;
|
||||
Result result = mStream->waitForStateChange(mStream->getState(), &next, timeout);
|
||||
EXPECT_EQ(Result::ErrorTimeout, result);
|
||||
}
|
||||
|
||||
void checkStopWhileWaiting() {
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
EXPECT_EQ(next, StreamState::Started) << "next = " << convertToText(next);
|
||||
|
||||
std::shared_ptr<AudioStream> str = mStream;
|
||||
|
||||
std::thread stopper([str] {
|
||||
usleep(200 * 1000);
|
||||
str->requestStop();
|
||||
});
|
||||
|
||||
r = mStream->waitForStateChange(StreamState::Started, &next, 1000 * kNanosPerMillisecond);
|
||||
stopper.join();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
// May have caught in stopping transition. Wait for full stop.
|
||||
if (next == StreamState::Stopping) {
|
||||
r = mStream->waitForStateChange(StreamState::Stopping, &next, 1000 * kNanosPerMillisecond);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
}
|
||||
ASSERT_EQ(next, StreamState::Stopped) << "next = " << convertToText(next);
|
||||
}
|
||||
|
||||
void checkCloseWhileWaiting() {
|
||||
StreamState next = StreamState::Unknown;
|
||||
auto r = mStream->requestStart();
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos);
|
||||
EXPECT_EQ(r, Result::OK);
|
||||
EXPECT_EQ(next, StreamState::Started) << "next = " << convertToText(next);
|
||||
|
||||
std::shared_ptr<AudioStream> str = mStream;
|
||||
|
||||
std::thread closer([str] {
|
||||
usleep(200 * 1000);
|
||||
str->close();
|
||||
});
|
||||
|
||||
r = mStream->waitForStateChange(StreamState::Started, &next, 1000 * kNanosPerMillisecond);
|
||||
closer.join();
|
||||
// You might catch this at any point in stopping or closing.
|
||||
EXPECT_TRUE(r == Result::OK || r == Result::ErrorClosed) << "r = " << convertToText(r);
|
||||
ASSERT_TRUE(next == StreamState::Stopping
|
||||
|| next == StreamState::Stopped
|
||||
|| next == StreamState::Pausing
|
||||
|| next == StreamState::Paused
|
||||
|| next == StreamState::Closed) << "next = " << convertToText(next);
|
||||
}
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
static constexpr int kTimeoutInNanos = 100 * kNanosPerMillisecond;
|
||||
|
||||
};
|
||||
|
||||
// Test return of error timeout when zero passed as the timeoutNanos.
|
||||
TEST_F(TestStreamWaitState, OutputLowWaitZero) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::LowLatency));
|
||||
checkWaitForStateChangeTimeout(0);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputNoneWaitZero) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::None));
|
||||
checkWaitForStateChangeTimeout(0);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputLowWaitZeroSLES) {
|
||||
AudioStreamBuilder builder;
|
||||
builder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
builder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream(builder));
|
||||
checkWaitForStateChangeTimeout(0);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputNoneWaitZeroSLES) {
|
||||
AudioStreamBuilder builder;
|
||||
builder.setPerformanceMode(PerformanceMode::None);
|
||||
builder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream(builder));
|
||||
checkWaitForStateChangeTimeout(0);
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
// Test actual timeout.
|
||||
TEST_F(TestStreamWaitState, OutputLowWaitNonZero) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::LowLatency));
|
||||
checkWaitForStateChangeTimeout();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputNoneWaitNonZero) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::None));
|
||||
checkWaitForStateChangeTimeout();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputLowWaitNonZeroSLES) {
|
||||
AudioStreamBuilder builder;
|
||||
builder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
builder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream(builder));
|
||||
checkWaitForStateChangeTimeout();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputNoneWaitNonZeroSLES) {
|
||||
AudioStreamBuilder builder;
|
||||
builder.setPerformanceMode(PerformanceMode::None);
|
||||
builder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream(builder));
|
||||
checkWaitForStateChangeTimeout();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputLowStopWhileWaiting) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::LowLatency));
|
||||
checkStopWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputNoneStopWhileWaiting) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::LowLatency));
|
||||
checkStopWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputLowStopWhileWaitingSLES) {
|
||||
AudioStreamBuilder builder;
|
||||
builder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
builder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream(builder));
|
||||
checkStopWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputLowCloseWhileWaiting) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::LowLatency));
|
||||
checkCloseWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputNoneCloseWhileWaiting) {
|
||||
ASSERT_TRUE(openStream(Direction::Output, PerformanceMode::None));
|
||||
checkCloseWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, InputLowCloseWhileWaiting) {
|
||||
ASSERT_TRUE(openStream(Direction::Input, PerformanceMode::LowLatency));
|
||||
checkCloseWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, InputNoneCloseWhileWaiting) {
|
||||
ASSERT_TRUE(openStream(Direction::Input, PerformanceMode::None));
|
||||
checkCloseWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputNoneCloseWhileWaitingSLES) {
|
||||
AudioStreamBuilder builder;
|
||||
builder.setPerformanceMode(PerformanceMode::None);
|
||||
builder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream(builder));
|
||||
checkCloseWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestStreamWaitState, OutputLowCloseWhileWaitingSLES) {
|
||||
AudioStreamBuilder builder;
|
||||
builder.setPerformanceMode(PerformanceMode::LowLatency);
|
||||
builder.setAudioApi(AudioApi::OpenSLES);
|
||||
ASSERT_TRUE(openStream(builder));
|
||||
checkCloseWhileWaiting();
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
40
externals/oboe/tests/testUtilities.cpp
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Definitions.h>
|
||||
#include <oboe/Utilities.h>
|
||||
|
||||
/**
|
||||
* Tests needing to be written:
|
||||
*
|
||||
* - oboe::convertFormatToSizeInBytes()
|
||||
*/
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
class UtilityFunctions : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(UtilityFunctions, Converts16BitIntegerToSizeOf2Bytes){
|
||||
int32_t sizeInBytes = oboe::convertFormatToSizeInBytes(AudioFormat::I16);
|
||||
ASSERT_EQ(sizeInBytes, 2);
|
||||
}
|
||||
|
||||
TEST_F(UtilityFunctions, ConvertsFloatToSizeOf4Bytes){
|
||||
int32_t sizeInBytes = oboe::convertFormatToSizeInBytes(AudioFormat::Float);
|
||||
ASSERT_EQ(sizeInBytes, 4);
|
||||
}
|
81
externals/oboe/tests/testXRunBehaviour.cpp
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
|
||||
class MyCallback : public AudioStreamCallback {
|
||||
public:
|
||||
DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
};
|
||||
|
||||
class XRunBehaviour : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
|
||||
bool openStream() {
|
||||
Result r = mBuilder.openStream(mStream);
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
bool closeStream() {
|
||||
Result r = mStream->close();
|
||||
EXPECT_EQ(r, Result::OK) << "Failed to close stream. " << convertToText(r);
|
||||
return (r == Result::OK);
|
||||
}
|
||||
|
||||
AudioStreamBuilder mBuilder;
|
||||
std::shared_ptr<AudioStream> mStream;
|
||||
|
||||
};
|
||||
|
||||
// TODO figure out this behaviour - On OpenSLES xRuns are supported within AudioStreamBuffered,
|
||||
// however, these aren't the same as the actual stream underruns
|
||||
TEST_F(XRunBehaviour, SupportedWhenStreamIsUsingAAudio){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
if (mStream->getAudioApi() == AudioApi::AAudio){
|
||||
ASSERT_TRUE(mStream->isXRunCountSupported());
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(XRunBehaviour, NotSupportedOnOpenSLESWhenStreamIsUsingCallback){
|
||||
|
||||
MyCallback callback;
|
||||
mBuilder.setCallback(&callback);
|
||||
ASSERT_TRUE(openStream());
|
||||
if (mStream->getAudioApi() == AudioApi::OpenSLES){
|
||||
ASSERT_FALSE(mStream->isXRunCountSupported());
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|
||||
|
||||
TEST_F(XRunBehaviour, SupportedOnOpenSLESWhenStreamIsUsingBlockingIO){
|
||||
|
||||
ASSERT_TRUE(openStream());
|
||||
if (mStream->getAudioApi() == AudioApi::OpenSLES){
|
||||
ASSERT_TRUE(mStream->isXRunCountSupported());
|
||||
}
|
||||
ASSERT_TRUE(closeStream());
|
||||
}
|