Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/OboeTester/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@
android:name=".TestDataPathsActivity"
android:label="@string/title_data_paths"
android:screenOrientation="portrait" />
<activity
android:name=".TestMultiStreamActivity"
android:exported="true"
android:label="Multi-Stream Test" />
<activity
android:name=".ExtraTestsActivity"
android:exported="true"
Expand Down
146 changes: 146 additions & 0 deletions apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ class ActivityContext {
return 0.0;
}

virtual double getPeakLevel(int streamIndex, int channelIndex) {
return 0.0;
}

static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
struct timespec time;
int result = clock_gettime(clockId, &time);
Expand Down Expand Up @@ -867,6 +871,142 @@ class ActivityTestDisconnect : public ActivityContext {
std::shared_ptr<oboe::flowgraph::SinkFloat> mSinkFloat;
};

/**
* Test multiple streams.
*/
class ActivityTestMultiStream : public ActivityContext {
public:
class MultiStreamCallback : public oboe::AudioStreamDataCallback {
public:
MultiStreamCallback() {
for (int i = 0; i < 8; i++) mPeakLevels[i] = 0.0;
}

oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) override {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is always not preferred to have tons of logic in header file.

int channelCount = audioStream->getChannelCount();
if (channelCount > 8) channelCount = 8;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why limit the count to 8? If it is limited, it needs to be aware of from the UI.


if (audioStream->getDirection() == oboe::Direction::Output) {
int channelCount = audioStream->getChannelCount();
float phaseIncrement = 440.0f * M_PI * 2 / (float)audioStream->getSampleRate();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: we don't have any common code of generating sine wave?

if (audioStream->getFormat() == oboe::AudioFormat::Float) {
float *floatData = (float *) audioData;
for (int i = 0; i < numFrames; i++) {
float sample = sinf(mPhase) * 0.5f;
mPhase += phaseIncrement;
if (mPhase > M_PI * 2) mPhase -= (float)(M_PI * 2);
for (int c = 0; c < channelCount; c++) {
*floatData++ = sample;
}
}
} else if (audioStream->getFormat() == oboe::AudioFormat::I16) {
int16_t *shortData = (int16_t *) audioData;
for (int i = 0; i < numFrames; i++) {
float sample = sinf(mPhase) * 0.5f;
mPhase += phaseIncrement;
if (mPhase > M_PI * 2) mPhase -= (float)(M_PI * 2);
for (int c = 0; c < channelCount; c++) {
*shortData++ = (int16_t)(sample * 32767);
}
}
} else if (audioStream->getFormat() == oboe::AudioFormat::I24) {
uint8_t *byteData = (uint8_t *) audioData;
for (int i = 0; i < numFrames; i++) {
float sample = sinf(mPhase) * 0.5f;
mPhase += phaseIncrement;
if (mPhase > M_PI * 2) mPhase -= (float)(M_PI * 2);
int32_t sample24 = (int32_t)(sample * 8388607);
for (int c = 0; c < channelCount; c++) {
*byteData++ = (uint8_t)(sample24 & 0xFF);
*byteData++ = (uint8_t)((sample24 >> 8) & 0xFF);
*byteData++ = (uint8_t)((sample24 >> 16) & 0xFF);
}
}
}
}

// Measure peak level for both input and output
if (audioStream->getFormat() == oboe::AudioFormat::Float) {
float *floatData = (float *) audioData;
for (int i = 0; i < numFrames; i++) {
for (int c = 0; c < channelCount; c++) {
float sample = std::abs(*floatData++);
if (sample > mPeakLevels[c]) mPeakLevels[c] = sample;
}
}
} else if (audioStream->getFormat() == oboe::AudioFormat::I16) {
int16_t *shortData = (int16_t *) audioData;
for (int i = 0; i < numFrames; i++) {
for (int c = 0; c < channelCount; c++) {
float sample = std::abs(*shortData++) / 32768.0f;
if (sample > mPeakLevels[c]) mPeakLevels[c] = sample;
}
}
} else if (audioStream->getFormat() == oboe::AudioFormat::I24) {
uint8_t *byteData = (uint8_t *) audioData;
for (int i = 0; i < numFrames; i++) {
for (int c = 0; c < channelCount; c++) {
int32_t sample24 = (byteData[0]) | (byteData[1] << 8) | (byteData[2] << 16);
if (sample24 & 0x800000) sample24 |= ~0xFFFFFF; // sign extend
float sample = std::abs(sample24) / 8388608.0f;
byteData += 3;
if (sample > mPeakLevels[c]) mPeakLevels[c] = sample;
}
}
}

return oboe::DataCallbackResult::Continue;
}

double getPeakLevel(int index) {
if (index < 0 || index >= 8) return 0.0;
double peak = mPeakLevels[index];
mPeakLevels[index] = 0.0;
return peak;
}
private:
float mPhase = 0.0f;
double mPeakLevels[8];
};

ActivityTestMultiStream() = default;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be removed.


virtual ~ActivityTestMultiStream() = default;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto.


void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override {
std::shared_ptr<MultiStreamCallback> callback = std::make_shared<MultiStreamCallback>();
mCallbacks.push_back(callback);
builder.setDataCallback(callback);
}

oboe::Result startStreams() override {
oboe::Result result = oboe::Result::OK;
for (auto entry : mOboeStreams) {
std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
if (oboeStream) {
result = oboeStream->requestStart();
if (result != oboe::Result::OK) break;
}
}
return result;
}

double getPeakLevel(int streamIndex, int channelIndex) override {
std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex);
if (oboeStream) {
oboe::AudioStreamDataCallback *callback = oboeStream->getDataCallback();
if (callback) {
MultiStreamCallback *myCallback = static_cast<MultiStreamCallback*>(callback);
return myCallback->getPeakLevel(channelIndex);
}
}
return 0.0;
}

private:
std::vector<std::shared_ptr<MultiStreamCallback>> mCallbacks;
};

/**
* Global context for native tests.
* Switch between various ActivityContexts.
Expand Down Expand Up @@ -910,6 +1050,9 @@ class NativeAudioContext {
case ActivityType::DataPath:
currentActivity = &mActivityDataPath;
break;
case ActivityType::TestMultiStream:
currentActivity = &mActivityTestMultiStream;
break;
}
}

Expand All @@ -926,6 +1069,7 @@ class NativeAudioContext {
ActivityGlitches mActivityGlitches;
ActivityDataPath mActivityDataPath;
ActivityTestDisconnect mActivityTestDisconnect;
ActivityTestMultiStream mActivityTestMultiStream;

private:

Expand All @@ -941,6 +1085,8 @@ class NativeAudioContext {
Glitches = 6,
TestDisconnect = 7,
DataPath = 8,
DynamicWorkload = 9,
TestMultiStream = 10,
};

ActivityType mActivityType = ActivityType::Undefined;
Expand Down
41 changes: 41 additions & 0 deletions apps/OboeTester/app/src/main/cpp/jni-bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ Java_com_mobileer_oboetester_OboeAudioStream_openNative(JNIEnv *env, jobject,
jint spatializationBehavior,
jstring packageName,
jstring attributionTag);
JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_startNative(JNIEnv *env, jobject, jint);
JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_pauseNative(JNIEnv *env, jobject, jint);
JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_stopNative(JNIEnv *env, jobject, jint);
JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_flushNative(JNIEnv *env, jobject, jint);
JNIEXPORT void JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_close(JNIEnv *env, jobject, jint);

Expand Down Expand Up @@ -313,6 +321,39 @@ Java_com_mobileer_oboetester_OboeAudioStream_startPlaybackNative(JNIEnv *env, jo
return (jint) engine.getCurrentActivity()->startPlayback();
}

JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_startNative(JNIEnv *env, jobject, jint streamIndex) {
std::shared_ptr<oboe::AudioStream> oboeStream = engine.getCurrentActivity()->getStream(streamIndex);
if (oboeStream) return (jint) oboeStream->requestStart();
return (jint) oboe::Result::ErrorNull;
}

JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_pauseNative(JNIEnv *env, jobject, jint streamIndex) {
std::shared_ptr<oboe::AudioStream> oboeStream = engine.getCurrentActivity()->getStream(streamIndex);
if (oboeStream) return (jint) oboeStream->requestPause();
return (jint) oboe::Result::ErrorNull;
}

JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_stopNative(JNIEnv *env, jobject, jint streamIndex) {
std::shared_ptr<oboe::AudioStream> oboeStream = engine.getCurrentActivity()->getStream(streamIndex);
if (oboeStream) return (jint) oboeStream->requestStop();
return (jint) oboe::Result::ErrorNull;
}

JNIEXPORT jint JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_flushNative(JNIEnv *env, jobject, jint streamIndex) {
std::shared_ptr<oboe::AudioStream> oboeStream = engine.getCurrentActivity()->getStream(streamIndex);
if (oboeStream) return (jint) oboeStream->requestFlush();
return (jint) oboe::Result::ErrorNull;
}

JNIEXPORT jdouble JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_getPeakLevelNative(JNIEnv *env, jobject, jint streamIndex, jint channelIndex) {
return engine.getCurrentActivity()->getPeakLevel(streamIndex, channelIndex);
}

JNIEXPORT void JNICALL
Java_com_mobileer_oboetester_OboeAudioStream_close(JNIEnv *env, jobject, jint streamIndex) {
engine.getCurrentActivity()->close(streamIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
class AudioInputTester extends AudioStreamTester{
private static AudioInputTester mInstance;

private AudioInputTester() {
public AudioInputTester() {
super();
Log.i(TapToToneActivity.TAG, "create OboeAudioStream ---------");

Expand All @@ -30,10 +30,7 @@ private AudioInputTester() {
}

public static synchronized AudioInputTester getInstance() {
if (mInstance == null) {
mInstance = new AudioInputTester();
}
return mInstance;
return new AudioInputTester();
}

public native double getPeakLevel(int i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@ public class AudioOutputTester extends AudioStreamTester {
private static AudioOutputTester mInstance;

public static synchronized AudioOutputTester getInstance() {
if (mInstance == null) {
mInstance = new AudioOutputTester();
}
return mInstance;
return new AudioOutputTester();
}

private AudioOutputTester() {
public AudioOutputTester() {
super();
Log.i(TapToToneActivity.TAG, "create OboeAudioOutputStream ---------");
mOboeAudioOutputStream = new OboeAudioOutputStream();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ public void onLaunchAudioWorkloadTestRunner(View view) {
public void onLaunchReverseJniTest(View view) {
launchTestActivity(ReverseJniActivity.class);
}

public void onLaunchTestMultiStream(View view) {
launchTestActivity(TestMultiStreamActivity.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,21 @@ public void close() {
}
public native void close(int streamIndex);

public int start() { return startNative(mStreamIndex); }
public native int startNative(int streamIndex);

public int pause() { return pauseNative(mStreamIndex); }
public native int pauseNative(int streamIndex);

public int stop() { return stopNative(mStreamIndex); }
public native int stopNative(int streamIndex);

public int flush() { return flushNative(mStreamIndex); }
public native int flushNative(int streamIndex);

public double getPeakLevel(int channelIndex) { return getPeakLevelNative(mStreamIndex, channelIndex); }
public native double getPeakLevelNative(int streamIndex, int channelIndex);

@Override
public int getBufferCapacityInFrames() {
return getBufferCapacityInFrames(mStreamIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ abstract class TestAudioActivity extends AppCompatActivity implements AudioManag
public static final int ACTIVITY_TEST_DISCONNECT = 7;
public static final int ACTIVITY_DATA_PATHS = 8;
public static final int ACTIVITY_DYNAMIC_WORKLOAD = 9;
public static final int ACTIVITY_TEST_MULTI_STREAM = 10;

private static final int MP3_RES_ID = R.raw.sine441stereo;
private static final AudioConfig MP3_FILE_CONFIG =
Expand Down Expand Up @@ -301,6 +302,9 @@ public int getServiceType() {
| ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
case ACTIVITY_DYNAMIC_WORKLOAD:
return ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
case ACTIVITY_TEST_MULTI_STREAM:
return ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
| ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
default:
Log.i(TAG, "getServiceType() called on unknown activity type " + getActivityType());
return 0;
Expand Down
Loading
Loading