From 0af81be62f04b231cebeda2f30cfe29b9a4fbc47 Mon Sep 17 00:00:00 2001 From: oskar Date: Fri, 23 Jan 2026 06:17:19 +0100 Subject: [PATCH] OboeTester needs volume report in Recorder (#2120) --- .../app/src/main/cpp/NativeAudioContext.cpp | 41 +++++++++++++++++++ .../app/src/main/cpp/NativeAudioContext.h | 9 ++++ .../app/src/main/cpp/jni-bridge.cpp | 35 ++++++++++++++++ .../mobileer/oboetester/RecorderActivity.java | 17 ++++++++ .../mobileer/oboetester/RecordingStats.java | 11 +++++ .../src/main/res/layout/activity_recorder.xml | 2 +- 6 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecordingStats.java diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp index e246b4ee1..ccf7f3031 100644 --- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp +++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp @@ -715,6 +715,47 @@ oboe::Result ActivityRecording::startPlayback() { return result; } +ActivityRecording::RecordingStats ActivityRecording::computeRecordingStats() { + constexpr int chunkFrames = 512; + + mRecording->rewind(); + + const int ch = mRecording->getChannelCount(); + auto buffer = std::make_unique(chunkFrames * ch); + + const int32_t totalFrames = mRecording->getSizeInFrames(); + int32_t frameIndex = 0; + + RecordingStats recordingStats; + + while (frameIndex < totalFrames) { + const int framesToRead = std::min(chunkFrames, totalFrames - frameIndex); + const int framesRead = mRecording->read(buffer.get(), framesToRead); + if (framesRead <= 0) break; + + const int samplesRead = framesRead * ch; + + for (int i = 0; i < samplesRead; ++i) { + const double x = buffer[i]; + recordingStats.peakAbs = std::max(recordingStats.peakAbs, std::abs(x)); + recordingStats.sumSq += x * x; + } + recordingStats.n += samplesRead; + + frameIndex += framesRead; + + bool lastRemainingFramesRead = framesRead < framesToRead; + if (lastRemainingFramesRead) break; + } + + return recordingStats; +} + +ActivityRecording::RecordingStats ActivityRecording::getRecordingStats() { + return computeRecordingStats(); +} + + // ======================================================================= ActivityTapToTone void ActivityTapToTone::configureAfterOpen() { monoToMulti = std::make_unique(mChannelCount); diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h index d58541f9b..70b1c4ec1 100644 --- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h +++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h @@ -456,6 +456,15 @@ class ActivityRecording : public ActivityTestInput { PlayRecordingCallback mPlayRecordingCallback; oboe::AudioStream *playbackStream = nullptr; + struct RecordingStats { + double peakAbs = 0.0; + double sumSq = 0.0; + int64_t n = 0; // total samples + }; + + RecordingStats computeRecordingStats(); + + ActivityRecording::RecordingStats getRecordingStats(); }; /** diff --git a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp index 8d5c01d78..6a6ae664e 100644 --- a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp +++ b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp @@ -59,6 +59,9 @@ static jfieldID g_stretchModeField = nullptr; static jfieldID g_pitchField = nullptr; static jfieldID g_speedField = nullptr; +static jclass g_recordingStatsClass = nullptr; +static jmethodID g_recordingStatsConstructor = nullptr; + JNIEXPORT jint JNICALL Java_com_mobileer_oboetester_OboeAudioStream_openNative(JNIEnv *env, jobject, jint nativeApi, @@ -1117,6 +1120,16 @@ Java_com_mobileer_oboetester_ManualGlitchActivity_getRecentSamples(JNIEnv *env, return numSamples; } +JNIEXPORT jobject JNICALL +Java_com_mobileer_oboetester_RecorderActivity_getRecordingStatsJni(JNIEnv *env, jobject) { + + ActivityRecording::RecordingStats params = engine.mActivityRecording.getRecordingStats(); + + return env->NewObject(g_recordingStatsClass, g_recordingStatsConstructor, + (jfloat)params.peakAbs, + (params.n > 0) ? (float)std::sqrt(params.sumSq / (float)params.n) : 0.0F); +} + JNIEXPORT void JNICALL Java_com_mobileer_oboetester_TestAudioActivity_setDefaultAudioValues(JNIEnv *env, jclass clazz, jint audio_manager_sample_rate, @@ -1401,6 +1414,28 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_ERR; } + const char* recordingStatsClassName = "com/mobileer/oboetester/RecordingStats"; + jclass localRecordingStatsClass = env->FindClass(recordingStatsClassName); + if (localRecordingStatsClass == nullptr) { + LOGE("JNI_OnLoad: Could not find class %s", recordingStatsClassName); + if (env->ExceptionCheck()) env->ExceptionDescribe(); + return JNI_ERR; + } + g_recordingStatsClass = (jclass)env->NewGlobalRef(localRecordingStatsClass); + env->DeleteLocalRef(localRecordingStatsClass); + if (g_recordingStatsClass == nullptr) { + LOGE("JNI_OnLoad: Could not create global ref for %s", recordingStatsClassName); + return JNI_ERR; + } + + g_recordingStatsConstructor = env->GetMethodID( + g_recordingStatsClass, "", "(FF)V"); + if (g_recordingStatsConstructor == nullptr) { + LOGE("JNI_OnLoad: Could not find constructor for %s", recordingStatsClassName); + if (env->ExceptionCheck()) env->ExceptionDescribe(); + return JNI_ERR; + } + g_fallbackModeField = env->GetFieldID(g_playbackParametersClass, "mFallbackMode", "I"); g_stretchModeField = env->GetFieldID(g_playbackParametersClass, "mStretchMode", "I"); g_pitchField = env->GetFieldID(g_playbackParametersClass, "mPitch", "F"); diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecorderActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecorderActivity.java index 48df2185c..c80bfed3c 100644 --- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecorderActivity.java +++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecorderActivity.java @@ -19,6 +19,7 @@ import android.os.Bundle; import android.view.View; import android.widget.Button; +import android.widget.TextView; import java.io.IOException; @@ -34,6 +35,7 @@ public class RecorderActivity extends TestInputActivity { private Button mStopButton; private Button mPlayButton; private Button mShareButton; + private TextView mStatusView; private boolean mGotRecording; @Override @@ -49,6 +51,7 @@ protected void onCreate(Bundle savedInstanceState) { mStopButton = (Button) findViewById(R.id.button_stop_record_play); mPlayButton = (Button) findViewById(R.id.button_start_playback); mShareButton = (Button) findViewById(R.id.button_share); + mStatusView = (TextView) findViewById(R.id.status_view); mRecorderState = AUDIO_STATE_STOPPED; mGotRecording = false; updateButtons(); @@ -75,6 +78,19 @@ public void onStopRecordPlay(View view) { closeAudio(); mRecorderState = AUDIO_STATE_STOPPED; updateButtons(); + + StringBuilder text = new StringBuilder(); + for (StreamContext streamContext: mStreamContexts) { + RecordingStats recordingStats = getRecordingStatsJni(); + text + .append("Peak amplitude: ") + .append(recordingStats.peak) + .append("\n") + .append("Rms: ") + .append(recordingStats.rms) + .append("\n"); + } + mStatusView.setText(text); } public void onStartPlayback(View view) { @@ -106,4 +122,5 @@ String getWaveTag() { return "recording"; } + private native RecordingStats getRecordingStatsJni(); } diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecordingStats.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecordingStats.java new file mode 100644 index 000000000..44bf8296f --- /dev/null +++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/RecordingStats.java @@ -0,0 +1,11 @@ +package com.mobileer.oboetester; + +public final class RecordingStats { + public final float peak; + public final float rms; + + public RecordingStats(float peak, float rms) { + this.peak = peak; + this.rms = rms; + } +} \ No newline at end of file diff --git a/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml b/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml index 83d8d2a20..ce08657dd 100644 --- a/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml +++ b/apps/OboeTester/app/src/main/res/layout/activity_recorder.xml @@ -98,7 +98,7 @@