Loading media/tests/EffectsTest/res/layout/visualizertest.xml +31 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,37 @@ </LinearLayout> <ImageView android:src="@android:drawable/divider_horizontal_dark" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scaleType="fitXY"/> <LinearLayout android:id="@+id/visuMultithreadedLayout" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginTop="10dip" android:layout_marginRight="10dip" android:layout_marginBottom="10dip" > <TextView android:id="@+id/visuMultithreaded" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1.0" android:layout_gravity="center_vertical|left" android:text="@string/effect_multithreaded" style="@android:style/TextAppearance.Medium" /> <ToggleButton android:id="@+id/visuMultithreadedOnOff" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="center_vertical|right" android:layout_weight="0.0" /> </LinearLayout> <ImageView android:src="@android:drawable/divider_horizontal_dark" android:layout_width="fill_parent" Loading media/tests/EffectsTest/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -35,4 +35,6 @@ <string name="effect_attach_off">Attach</string> <string name="effect_attach_on">Detach</string> <string name="send_level_name">Send Level</string> <!-- Toggles use of a multi-threaded client for an effect [CHAR LIMIT=24] --> <string name="effect_multithreaded">Multithreaded Use</string> </resources> media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstance.java 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package com.android.effectstest; interface VisualizerInstance { void enableDataCaptureListener(boolean enable); boolean getEnabled(); void release(); void setEnabled(boolean enabled); void startStopCapture(boolean start); } media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceMT.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package com.android.effectstest; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.android.internal.annotations.GuardedBy; class VisualizerInstanceMT implements VisualizerInstance { private static final String TAG = "VisualizerInstanceMT"; private final Object mLock = new Object(); private final int mThreadCount; @GuardedBy("mLock") private Handler mVisualizerHandler; @GuardedBy("mLock") private VisualizerInstanceSync mVisualizer; VisualizerInstanceMT(int session, Handler uiHandler, int extraThreadCount) { Log.d(TAG, "Multi-threaded constructor"); mThreadCount = 1 + extraThreadCount; Thread t = new Thread() { @Override public void run() { Looper.prepare(); VisualizerInstanceSync v = new VisualizerInstanceSync(session, uiHandler); synchronized (mLock) { mVisualizerHandler = new Handler(); mVisualizer = v; } Looper.loop(); } }; t.start(); } private VisualizerInstance getVisualizer() { synchronized (mLock) { return mVisualizer != null ? new VisualizerInstanceSync(mVisualizer) : null; } } private interface VisualizerOperation { void run(VisualizerInstance v); } private void runOperationMt(VisualizerOperation op) { final VisualizerInstance v = getVisualizer(); if (v == null) return; for (int i = 0; i < mThreadCount; ++i) { Thread t = new Thread() { @Override public void run() { op.run(v); } }; t.start(); } } @Override public void enableDataCaptureListener(boolean enable) { runOperationMt(v -> v.enableDataCaptureListener(enable)); } @Override public boolean getEnabled() { final VisualizerInstance v = getVisualizer(); return v != null ? v.getEnabled() : false; } @Override public void release() { runOperationMt(v -> v.release()); synchronized (mLock) { if (mVisualizerHandler == null) return; mVisualizerHandler.post(() -> { synchronized (mLock) { mVisualizerHandler = null; mVisualizer = null; Looper.myLooper().quitSafely(); } Log.d(TAG, "Exiting looper"); }); } } @Override public void setEnabled(boolean enabled) { runOperationMt(v -> v.setEnabled(enabled)); } @Override public void startStopCapture(boolean start) { runOperationMt(v -> v.startStopCapture(start)); } } media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceSync.java 0 → 100644 +170 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package com.android.effectstest; import android.media.audiofx.Visualizer; import android.os.Handler; import android.os.Message; import android.util.Log; // This class only has `final' members, thus any thread-safety concerns // can only come from the Visualizer effect class. class VisualizerInstanceSync implements VisualizerInstance { private static final String TAG = "VisualizerInstance"; private final Handler mUiHandler; private final Visualizer mVisualizer; private final VisualizerTestHandler mVisualizerTestHandler; private final VisualizerListener mVisualizerListener; VisualizerInstanceSync(int session, Handler uiHandler) { mUiHandler = uiHandler; try { mVisualizer = new Visualizer(session); } catch (UnsupportedOperationException e) { Log.e(TAG, "Visualizer library not loaded"); throw new RuntimeException("Cannot initialize effect"); } catch (RuntimeException e) { throw e; } mVisualizerTestHandler = new VisualizerTestHandler(); mVisualizerListener = new VisualizerListener(); } // Not a "deep" copy, only copies the references. VisualizerInstanceSync(VisualizerInstanceSync other) { mUiHandler = other.mUiHandler; mVisualizer = other.mVisualizer; mVisualizerTestHandler = other.mVisualizerTestHandler; mVisualizerListener = other.mVisualizerListener; } @Override public void enableDataCaptureListener(boolean enable) { mVisualizer.setDataCaptureListener(enable ? mVisualizerListener : null, 10000, enable, enable); } @Override public boolean getEnabled() { return mVisualizer.getEnabled(); } @Override public void release() { mVisualizer.release(); Log.d(TAG, "Visualizer released"); } @Override public void setEnabled(boolean enabled) { mVisualizer.setEnabled(enabled); } @Override public void startStopCapture(boolean start) { mVisualizerTestHandler.sendMessage(mVisualizerTestHandler.obtainMessage( start ? MSG_START_CAPTURE : MSG_STOP_CAPTURE)); } private static final int MSG_START_CAPTURE = 0; private static final int MSG_STOP_CAPTURE = 1; private static final int MSG_NEW_CAPTURE = 2; private static final int CAPTURE_PERIOD_MS = 100; private static int[] dataToMinMaxCenter(byte[] data, int len) { int[] minMaxCenter = new int[3]; minMaxCenter[0] = data[0]; minMaxCenter[1] = data[len - 1]; minMaxCenter[2] = data[len / 2]; return minMaxCenter; } private class VisualizerTestHandler extends Handler { private final int mCaptureSize; private boolean mActive = false; VisualizerTestHandler() { mCaptureSize = mVisualizer.getCaptureSize(); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_START_CAPTURE: if (!mActive) { Log.d(TAG, "Start capture"); mActive = true; sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS); } break; case MSG_STOP_CAPTURE: if (mActive) { Log.d(TAG, "Stop capture"); mActive = false; } break; case MSG_NEW_CAPTURE: if (mActive) { if (mCaptureSize > 0) { byte[] data = new byte[mCaptureSize]; if (mVisualizer.getWaveForm(data) == Visualizer.SUCCESS) { int len = data.length < mCaptureSize ? data.length : mCaptureSize; mUiHandler.sendMessage( mUiHandler.obtainMessage( VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL, dataToMinMaxCenter(data, len))); } if (mVisualizer.getFft(data) == Visualizer.SUCCESS) { int len = data.length < mCaptureSize ? data.length : mCaptureSize; mUiHandler.sendMessage( mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL, dataToMinMaxCenter(data, len))); } } sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS); } break; } } } private class VisualizerListener implements Visualizer.OnDataCaptureListener { @Override public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) { if (visualizer == mVisualizer && waveform.length > 0) { Log.d(TAG, "onWaveFormDataCapture(): " + waveform[0] + " smp rate: " + samplingRate / 1000); mUiHandler.sendMessage( mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL, dataToMinMaxCenter(waveform, waveform.length))); } } @Override public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) { if (visualizer == mVisualizer && fft.length > 0) { Log.d(TAG, "onFftDataCapture(): " + fft[0]); mUiHandler.sendMessage( mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL, dataToMinMaxCenter(fft, fft.length))); } } } } Loading
media/tests/EffectsTest/res/layout/visualizertest.xml +31 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,37 @@ </LinearLayout> <ImageView android:src="@android:drawable/divider_horizontal_dark" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scaleType="fitXY"/> <LinearLayout android:id="@+id/visuMultithreadedLayout" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginTop="10dip" android:layout_marginRight="10dip" android:layout_marginBottom="10dip" > <TextView android:id="@+id/visuMultithreaded" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1.0" android:layout_gravity="center_vertical|left" android:text="@string/effect_multithreaded" style="@android:style/TextAppearance.Medium" /> <ToggleButton android:id="@+id/visuMultithreadedOnOff" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="center_vertical|right" android:layout_weight="0.0" /> </LinearLayout> <ImageView android:src="@android:drawable/divider_horizontal_dark" android:layout_width="fill_parent" Loading
media/tests/EffectsTest/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -35,4 +35,6 @@ <string name="effect_attach_off">Attach</string> <string name="effect_attach_on">Detach</string> <string name="send_level_name">Send Level</string> <!-- Toggles use of a multi-threaded client for an effect [CHAR LIMIT=24] --> <string name="effect_multithreaded">Multithreaded Use</string> </resources>
media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstance.java 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package com.android.effectstest; interface VisualizerInstance { void enableDataCaptureListener(boolean enable); boolean getEnabled(); void release(); void setEnabled(boolean enabled); void startStopCapture(boolean start); }
media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceMT.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package com.android.effectstest; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.android.internal.annotations.GuardedBy; class VisualizerInstanceMT implements VisualizerInstance { private static final String TAG = "VisualizerInstanceMT"; private final Object mLock = new Object(); private final int mThreadCount; @GuardedBy("mLock") private Handler mVisualizerHandler; @GuardedBy("mLock") private VisualizerInstanceSync mVisualizer; VisualizerInstanceMT(int session, Handler uiHandler, int extraThreadCount) { Log.d(TAG, "Multi-threaded constructor"); mThreadCount = 1 + extraThreadCount; Thread t = new Thread() { @Override public void run() { Looper.prepare(); VisualizerInstanceSync v = new VisualizerInstanceSync(session, uiHandler); synchronized (mLock) { mVisualizerHandler = new Handler(); mVisualizer = v; } Looper.loop(); } }; t.start(); } private VisualizerInstance getVisualizer() { synchronized (mLock) { return mVisualizer != null ? new VisualizerInstanceSync(mVisualizer) : null; } } private interface VisualizerOperation { void run(VisualizerInstance v); } private void runOperationMt(VisualizerOperation op) { final VisualizerInstance v = getVisualizer(); if (v == null) return; for (int i = 0; i < mThreadCount; ++i) { Thread t = new Thread() { @Override public void run() { op.run(v); } }; t.start(); } } @Override public void enableDataCaptureListener(boolean enable) { runOperationMt(v -> v.enableDataCaptureListener(enable)); } @Override public boolean getEnabled() { final VisualizerInstance v = getVisualizer(); return v != null ? v.getEnabled() : false; } @Override public void release() { runOperationMt(v -> v.release()); synchronized (mLock) { if (mVisualizerHandler == null) return; mVisualizerHandler.post(() -> { synchronized (mLock) { mVisualizerHandler = null; mVisualizer = null; Looper.myLooper().quitSafely(); } Log.d(TAG, "Exiting looper"); }); } } @Override public void setEnabled(boolean enabled) { runOperationMt(v -> v.setEnabled(enabled)); } @Override public void startStopCapture(boolean start) { runOperationMt(v -> v.startStopCapture(start)); } }
media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceSync.java 0 → 100644 +170 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package com.android.effectstest; import android.media.audiofx.Visualizer; import android.os.Handler; import android.os.Message; import android.util.Log; // This class only has `final' members, thus any thread-safety concerns // can only come from the Visualizer effect class. class VisualizerInstanceSync implements VisualizerInstance { private static final String TAG = "VisualizerInstance"; private final Handler mUiHandler; private final Visualizer mVisualizer; private final VisualizerTestHandler mVisualizerTestHandler; private final VisualizerListener mVisualizerListener; VisualizerInstanceSync(int session, Handler uiHandler) { mUiHandler = uiHandler; try { mVisualizer = new Visualizer(session); } catch (UnsupportedOperationException e) { Log.e(TAG, "Visualizer library not loaded"); throw new RuntimeException("Cannot initialize effect"); } catch (RuntimeException e) { throw e; } mVisualizerTestHandler = new VisualizerTestHandler(); mVisualizerListener = new VisualizerListener(); } // Not a "deep" copy, only copies the references. VisualizerInstanceSync(VisualizerInstanceSync other) { mUiHandler = other.mUiHandler; mVisualizer = other.mVisualizer; mVisualizerTestHandler = other.mVisualizerTestHandler; mVisualizerListener = other.mVisualizerListener; } @Override public void enableDataCaptureListener(boolean enable) { mVisualizer.setDataCaptureListener(enable ? mVisualizerListener : null, 10000, enable, enable); } @Override public boolean getEnabled() { return mVisualizer.getEnabled(); } @Override public void release() { mVisualizer.release(); Log.d(TAG, "Visualizer released"); } @Override public void setEnabled(boolean enabled) { mVisualizer.setEnabled(enabled); } @Override public void startStopCapture(boolean start) { mVisualizerTestHandler.sendMessage(mVisualizerTestHandler.obtainMessage( start ? MSG_START_CAPTURE : MSG_STOP_CAPTURE)); } private static final int MSG_START_CAPTURE = 0; private static final int MSG_STOP_CAPTURE = 1; private static final int MSG_NEW_CAPTURE = 2; private static final int CAPTURE_PERIOD_MS = 100; private static int[] dataToMinMaxCenter(byte[] data, int len) { int[] minMaxCenter = new int[3]; minMaxCenter[0] = data[0]; minMaxCenter[1] = data[len - 1]; minMaxCenter[2] = data[len / 2]; return minMaxCenter; } private class VisualizerTestHandler extends Handler { private final int mCaptureSize; private boolean mActive = false; VisualizerTestHandler() { mCaptureSize = mVisualizer.getCaptureSize(); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_START_CAPTURE: if (!mActive) { Log.d(TAG, "Start capture"); mActive = true; sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS); } break; case MSG_STOP_CAPTURE: if (mActive) { Log.d(TAG, "Stop capture"); mActive = false; } break; case MSG_NEW_CAPTURE: if (mActive) { if (mCaptureSize > 0) { byte[] data = new byte[mCaptureSize]; if (mVisualizer.getWaveForm(data) == Visualizer.SUCCESS) { int len = data.length < mCaptureSize ? data.length : mCaptureSize; mUiHandler.sendMessage( mUiHandler.obtainMessage( VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL, dataToMinMaxCenter(data, len))); } if (mVisualizer.getFft(data) == Visualizer.SUCCESS) { int len = data.length < mCaptureSize ? data.length : mCaptureSize; mUiHandler.sendMessage( mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL, dataToMinMaxCenter(data, len))); } } sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS); } break; } } } private class VisualizerListener implements Visualizer.OnDataCaptureListener { @Override public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) { if (visualizer == mVisualizer && waveform.length > 0) { Log.d(TAG, "onWaveFormDataCapture(): " + waveform[0] + " smp rate: " + samplingRate / 1000); mUiHandler.sendMessage( mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL, dataToMinMaxCenter(waveform, waveform.length))); } } @Override public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) { if (visualizer == mVisualizer && fft.length > 0) { Log.d(TAG, "onFftDataCapture(): " + fft[0]); mUiHandler.sendMessage( mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL, dataToMinMaxCenter(fft, fft.length))); } } } }