1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.effectstest; 18 19 import android.media.audiofx.Visualizer; 20 import android.os.Handler; 21 import android.os.Message; 22 import android.util.Log; 23 24 // This class only has `final' members, thus any thread-safety concerns 25 // can only come from the Visualizer effect class. 26 class VisualizerInstanceSync implements VisualizerInstance { 27 28 private static final String TAG = "VisualizerInstance"; 29 30 private final Handler mUiHandler; 31 private final Visualizer mVisualizer; 32 private final VisualizerTestHandler mVisualizerTestHandler; 33 private final VisualizerListener mVisualizerListener; 34 VisualizerInstanceSync(int session, Handler uiHandler)35 VisualizerInstanceSync(int session, Handler uiHandler) { 36 mUiHandler = uiHandler; 37 try { 38 mVisualizer = new Visualizer(session); 39 } catch (UnsupportedOperationException e) { 40 Log.e(TAG, "Visualizer library not loaded"); 41 throw new RuntimeException("Cannot initialize effect"); 42 } catch (RuntimeException e) { 43 throw e; 44 } 45 mVisualizerTestHandler = new VisualizerTestHandler(); 46 mVisualizerListener = new VisualizerListener(); 47 } 48 49 // Not a "deep" copy, only copies the references. VisualizerInstanceSync(VisualizerInstanceSync other)50 VisualizerInstanceSync(VisualizerInstanceSync other) { 51 mUiHandler = other.mUiHandler; 52 mVisualizer = other.mVisualizer; 53 mVisualizerTestHandler = other.mVisualizerTestHandler; 54 mVisualizerListener = other.mVisualizerListener; 55 } 56 57 @Override enableDataCaptureListener(boolean enable)58 public void enableDataCaptureListener(boolean enable) { 59 mVisualizer.setDataCaptureListener(enable ? mVisualizerListener : null, 60 10000, enable, enable); 61 } 62 63 @Override getEnabled()64 public boolean getEnabled() { 65 return mVisualizer.getEnabled(); 66 } 67 68 @Override release()69 public void release() { 70 mVisualizer.release(); 71 Log.d(TAG, "Visualizer released"); 72 } 73 74 @Override setEnabled(boolean enabled)75 public void setEnabled(boolean enabled) { 76 mVisualizer.setEnabled(enabled); 77 } 78 79 @Override startStopCapture(boolean start)80 public void startStopCapture(boolean start) { 81 mVisualizerTestHandler.sendMessage(mVisualizerTestHandler.obtainMessage( 82 start ? MSG_START_CAPTURE : MSG_STOP_CAPTURE)); 83 } 84 85 private static final int MSG_START_CAPTURE = 0; 86 private static final int MSG_STOP_CAPTURE = 1; 87 private static final int MSG_NEW_CAPTURE = 2; 88 private static final int CAPTURE_PERIOD_MS = 100; 89 dataToMinMaxCenter(byte[] data, int len)90 private static int[] dataToMinMaxCenter(byte[] data, int len) { 91 int[] minMaxCenter = new int[3]; 92 minMaxCenter[0] = data[0]; 93 minMaxCenter[1] = data[len - 1]; 94 minMaxCenter[2] = data[len / 2]; 95 return minMaxCenter; 96 } 97 98 private class VisualizerTestHandler extends Handler { 99 private final int mCaptureSize; 100 private boolean mActive = false; 101 VisualizerTestHandler()102 VisualizerTestHandler() { 103 mCaptureSize = mVisualizer.getCaptureSize(); 104 } 105 106 @Override handleMessage(Message msg)107 public void handleMessage(Message msg) { 108 switch (msg.what) { 109 case MSG_START_CAPTURE: 110 if (!mActive) { 111 Log.d(TAG, "Start capture"); 112 mActive = true; 113 sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS); 114 } 115 break; 116 case MSG_STOP_CAPTURE: 117 if (mActive) { 118 Log.d(TAG, "Stop capture"); 119 mActive = false; 120 } 121 break; 122 case MSG_NEW_CAPTURE: 123 if (mActive) { 124 if (mCaptureSize > 0) { 125 byte[] data = new byte[mCaptureSize]; 126 if (mVisualizer.getWaveForm(data) == Visualizer.SUCCESS) { 127 int len = data.length < mCaptureSize ? data.length : mCaptureSize; 128 mUiHandler.sendMessage( 129 mUiHandler.obtainMessage( 130 VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL, 131 dataToMinMaxCenter(data, len))); 132 } 133 if (mVisualizer.getFft(data) == Visualizer.SUCCESS) { 134 int len = data.length < mCaptureSize ? data.length : mCaptureSize; 135 mUiHandler.sendMessage( 136 mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL, 137 dataToMinMaxCenter(data, len))); 138 } 139 } 140 sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS); 141 } 142 break; 143 } 144 } 145 } 146 147 private class VisualizerListener implements Visualizer.OnDataCaptureListener { 148 @Override 149 public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, 150 int samplingRate) { 151 if (visualizer == mVisualizer && waveform.length > 0) { 152 Log.d(TAG, "onWaveFormDataCapture(): " + waveform[0] 153 + " smp rate: " + samplingRate / 1000); 154 mUiHandler.sendMessage( 155 mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL, 156 dataToMinMaxCenter(waveform, waveform.length))); 157 } 158 } 159 160 @Override 161 public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) { 162 if (visualizer == mVisualizer && fft.length > 0) { 163 Log.d(TAG, "onFftDataCapture(): " + fft[0]); 164 mUiHandler.sendMessage( 165 mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL, 166 dataToMinMaxCenter(fft, fft.length))); 167 } 168 } 169 } 170 } 171