DrumThumper: hack latency buttons To demo latency at Media Summit
diff --git a/samples/drumthumper/src/main/cpp/DrumPlayerJNI.cpp b/samples/drumthumper/src/main/cpp/DrumPlayerJNI.cpp index 44f546f..698ab5d 100644 --- a/samples/drumthumper/src/main/cpp/DrumPlayerJNI.cpp +++ b/samples/drumthumper/src/main/cpp/DrumPlayerJNI.cpp
@@ -135,6 +135,8 @@ */ JNIEXPORT void JNICALL Java_com_plausiblesoftware_drumthumper_DrumPlayer_restartStream(JNIEnv*, jobject) { sDTPlayer.resetAll(); + sDTPlayer.teardownAudioStream(); + usleep(40 * 1000); // give samples time to stop if (sDTPlayer.openStream() && sDTPlayer.startStream()){ __android_log_print(ANDROID_LOG_INFO, TAG, "openStream successful"); } else { @@ -162,6 +164,18 @@ return sDTPlayer.getGain(index); } +JNIEXPORT void JNICALL +Java_com_plausiblesoftware_drumthumper_DrumPlayer_setLatencyMode(JNIEnv *env, jobject thiz, + jint latencyMode) { + const int LATENCY_LOW_MMAP = 0; // Must match definitions in DrumPlayer.java + const int LATENCY_NORMAL = 2; + sDTPlayer.setMMapEnabled(latencyMode == LATENCY_LOW_MMAP); + sDTPlayer.setPerformanceMode( + (latencyMode == LATENCY_NORMAL) + ? oboe::PerformanceMode::None + : oboe::PerformanceMode::LowLatency); +} + #ifdef __cplusplus } #endif
diff --git a/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumPlayer.kt b/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumPlayer.kt index c47f674..b3e1f2c 100644 --- a/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumPlayer.kt +++ b/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumPlayer.kt
@@ -45,6 +45,10 @@ val PAN_HIHATOPEN: Float = -1.0f // Hard Left val PAN_HIHATCLOSED: Float = -1.0f // Hard Left + val LATENCY_LOW_MMAP = 0 + val LATENCY_LOW_LEGACY = 1 + val LATENCY_NORMAL = 2 + // Logging Tag val TAG: String = "DrumPlayer" } @@ -111,4 +115,6 @@ external fun clearOutputReset() external fun restartStream() + + external fun setLatencyMode(latncyMode: Int) }
diff --git a/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumThumperActivity.kt b/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumThumperActivity.kt index ffb3030..65d7d2c 100644 --- a/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumThumperActivity.kt +++ b/samples/drumthumper/src/main/kotlin/com/plausibleaudio/drumthumper/DrumThumperActivity.kt
@@ -24,6 +24,7 @@ import android.view.View import android.widget.Button import android.widget.LinearLayout +import android.widget.RadioButton import android.widget.SeekBar import android.widget.Toast import androidx.appcompat.app.AppCompatActivity @@ -159,13 +160,43 @@ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + // UI + setContentView(R.layout.drumthumper_activity) + mAudioMgr = getSystemService(Context.AUDIO_SERVICE) as AudioManager + val mRadioLatency0 = findViewById<View>(R.id.latencyMode0) as RadioButton + mRadioLatency0.setOnClickListener(View.OnClickListener { view -> + onLatencyRadioButtonClicked(view, 0) + }) + + val mRadioLatency1 = findViewById<View>(R.id.latencyMode1) as RadioButton? + if (mRadioLatency1 != null) { + mRadioLatency1.setOnClickListener(View.OnClickListener { view -> + onLatencyRadioButtonClicked(view, 1) + }) + } + + val mRadioLatency2 = findViewById<View>(R.id.latencyMode2) as RadioButton? + if (mRadioLatency2 != null) { + mRadioLatency2.setOnClickListener(View.OnClickListener { view -> + onLatencyRadioButtonClicked(view, 2) + }) + } + } + + private fun onLatencyRadioButtonClicked(view: View, mode: Int) { + val checked = (view as RadioButton).isChecked + if (!checked) return + mDrumPlayer.setLatencyMode(mode) + mDrumPlayer.restartStream() } override fun onStart() { super.onStart() + mDrumPlayer.setLatencyMode(DrumPlayer.LATENCY_LOW_MMAP) + mDrumPlayer.setupAudioStream() mDrumPlayer.loadWavAssets(getAssets()) @@ -180,9 +211,6 @@ override fun onResume() { super.onResume() - // UI - setContentView(R.layout.drumthumper_activity) - // "Kick" drum findViewById<TriggerPad>(R.id.kickPad).addListener(this) connectMixSliders(R.id.kickPan, R.id.kickGain, DrumPlayer.BASSDRUM)
diff --git a/samples/drumthumper/src/main/res/layout-land/drumthumper_activity.xml b/samples/drumthumper/src/main/res/layout-land/drumthumper_activity.xml index b657738..7e934b5 100644 --- a/samples/drumthumper/src/main/res/layout-land/drumthumper_activity.xml +++ b/samples/drumthumper/src/main/res/layout-land/drumthumper_activity.xml
@@ -491,9 +491,54 @@ </LinearLayout> </LinearLayout> </LinearLayout> - <Button + + <LinearLayout + android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Hide/Show Mix Controls" - android:id="@+id/mixCtrlBtn"/> + android:layout_height="match_parent"> + + <Button + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hide/Show Mix Controls" + android:id="@+id/mixCtrlBtn"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20dp" + android:text="Latency:" /> + + <RadioGroup + android:id="@+id/latencyModeGroup" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <RadioButton + android:id="@+id/latencyMode0" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:checked="true" + android:textSize="20sp" + android:text="MMAP" + /> + + <RadioButton + android:id="@+id/latencyMode1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20dp" + android:text="Legacy" + /> + + <RadioButton + android:id="@+id/latencyMode2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20dp" + android:text="None" + /> + </RadioGroup> + </LinearLayout> </LinearLayout>
diff --git a/samples/drumthumper/src/main/res/layout/drumthumper_activity.xml b/samples/drumthumper/src/main/res/layout/drumthumper_activity.xml index 1c0573b..cc2b998 100644 --- a/samples/drumthumper/src/main/res/layout/drumthumper_activity.xml +++ b/samples/drumthumper/src/main/res/layout/drumthumper_activity.xml
@@ -508,4 +508,49 @@ android:layout_height="wrap_content" android:text="Hide/Show Mix Controls" android:id="@+id/mixCtrlBtn"/> + + <LinearLayout + android:orientation="horizontal" + android:padding="20dp" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20dp" + android:text="Latency:" /> + + <RadioGroup + android:id="@+id/latencyModeGroup" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <RadioButton + android:id="@+id/latencyMode0" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:checked="true" + android:textSize="20sp" + android:text="MMAP" + /> + + <RadioButton + android:id="@+id/latencyMode1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20dp" + android:text="Legacy" + /> + + <RadioButton + android:id="@+id/latencyMode2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20dp" + android:text="None" + /> + </RadioGroup> + </LinearLayout> </LinearLayout>
diff --git a/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp b/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp index 4948d88..22586ee 100644 --- a/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp +++ b/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp
@@ -85,11 +85,14 @@ // we will resample source data to device rate, so take default sample rate builder.setDataCallback(mDataCallback); builder.setErrorCallback(mErrorCallback); - builder.setPerformanceMode(PerformanceMode::LowLatency); + builder.setPerformanceMode(mPerformanceMode); builder.setSharingMode(SharingMode::Exclusive); - builder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium); +// builder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium); + bool wasMMapEnabled = OboeExtensions::isMMapEnabled(); + OboeExtensions::setMMapEnabled(mMMapEnabled); Result result = builder.openStream(mAudioStream); + OboeExtensions::setMMapEnabled(wasMMapEnabled); if (result != Result::OK){ __android_log_print( ANDROID_LOG_ERROR, @@ -119,7 +122,7 @@ int tryCount = 0; while (tryCount < 3) { bool wasOpenSuccessful = true; - // Assume that apenStream() was called successfully before startStream() call. + // Assume that openStream() was called successfully before startStream() call. if (tryCount > 0) { usleep(20 * 1000); // Sleep between tries to give the system time to settle. wasOpenSuccessful = openStream(); // Try to open the stream again after the first try.
diff --git a/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h b/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h index b388b8f..82ef106 100644 --- a/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h +++ b/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h
@@ -68,6 +68,22 @@ void setGain(int index, float gain); float getGain(int index); + oboe::PerformanceMode getPerformanceMode() const { + return mPerformanceMode; + } + + void setPerformanceMode(oboe::PerformanceMode performanceMode) { + mPerformanceMode = performanceMode; + } + + bool isMMapEnabled() const { + return mMMapEnabled; + } + + void setMMapEnabled(bool enabled) { + mMMapEnabled = enabled; + } + private: class MyDataCallback : public oboe::AudioStreamDataCallback { public: @@ -101,6 +117,10 @@ // Playback Audio attributes int32_t mChannelCount; int32_t mSampleRate; + oboe::PerformanceMode mPerformanceMode = oboe::PerformanceMode::LowLatency; + +private: + bool mMMapEnabled = false; // Sample Data int32_t mNumSampleBuffers;