/*
* Copyright (C) 2014 Haruki Hasegawa
*
* 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.h6ah4i.android.media.opensl.audiofx;
import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.h6ah4i.android.media.audiofx.IVisualizer;
import com.h6ah4i.android.media.opensl.OpenSLMediaPlayer;
import com.h6ah4i.android.media.opensl.OpenSLMediaPlayerContext;
import com.h6ah4i.android.media.opensl.OpenSLMediaPlayerNativeLibraryLoader;
public class OpenSLVisualizer implements IVisualizer {
private static final String TAG = "Visualizer";
private long mNativeHandle;
private static final boolean HAS_NATIVE;
private int[] mParamIntBuff = new int[2];
private boolean[] mParamBoolBuff = new boolean[1];
private volatile InternalHandler mHandler;
private volatile OnDataCaptureListener mOnDataCaptureListener;
static {
// load native library
HAS_NATIVE = OpenSLMediaPlayerNativeLibraryLoader.loadLibraries();
}
public static int[] sGetCaptureSizeRange() {
int[] range = new int[2];
int result = getCaptureSizeRangeImplNative(range);
throwIllegalStateExceptionIfNeeded(result);
return range;
}
public static int sGetMaxCaptureRate() {
int[] rate = new int[1];
int result = getMaxCaptureRateImplNative(rate);
throwIllegalStateExceptionIfNeeded(result);
return rate[0];
}
public OpenSLVisualizer(OpenSLMediaPlayerContext context) {
if (context == null)
throw new IllegalArgumentException("The argument 'context' cannot be null");
if (HAS_NATIVE) {
mNativeHandle = createNativeImplHandle(
OpenSLMediaPlayer.Internal.getNativeHandle(context),
new WeakReference<OpenSLVisualizer>(this));
}
if (mNativeHandle == 0) {
throw new UnsupportedOperationException("Failed to initialize native layer");
}
mHandler = new InternalHandler(this);
}
@Override
protected void finalize() throws Throwable {
release();
super.finalize();
}
@Override
public void release() {
mOnDataCaptureListener = null;
try {
if (HAS_NATIVE && mNativeHandle != 0) {
deleteNativeImplHandle(mNativeHandle);
mNativeHandle = 0;
}
} catch (Exception e) {
Log.e(TAG, "release()", e);
}
if (mHandler != null) {
mHandler.release();
mHandler = null;
}
}
@Override
public boolean getEnabled() {
checkNativeImplIsAvailable();
final boolean[] enabled = mParamBoolBuff;
int result = getEnabledImplNative(mNativeHandle, enabled);
throwIllegalStateExceptionIfNeeded(result);
return enabled[0];
}
@Override
public int setEnabled(boolean enabled) throws IllegalStateException {
checkNativeImplIsAvailable();
int result = setEnabledImplNative(mNativeHandle, enabled);
throwIllegalStateExceptionIfNeeded(result);
return translateErrorCode(result);
}
@Override
public int getSamplingRate() throws IllegalStateException {
checkNativeImplIsAvailable();
final int[] samplingRate = mParamIntBuff;
int result = getSamplingRateImplNative(mNativeHandle, samplingRate);
throwIllegalStateExceptionIfNeeded(result);
return samplingRate[0];
}
@Override
public int getFft(byte[] fft) throws IllegalStateException {
checkNativeImplIsAvailable();
int result = getFftImplNative(mNativeHandle, fft);
if (result == OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_STATE) {
throw new IllegalStateException("getFft() called while unexpected state");
}
return translateErrorCode(result);
}
@Override
public int getWaveForm(byte[] waveform) throws IllegalStateException {
checkNativeImplIsAvailable();
int result = getWaveformImplNative(mNativeHandle, waveform);
if (result == OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_STATE) {
throw new IllegalStateException("getWaveForm() called while unexpected state");
}
return translateErrorCode(result);
}
@Override
public int getCaptureSize() throws IllegalStateException {
checkNativeImplIsAvailable();
final int[] size = mParamIntBuff;
int result = getCaptureSizeImplNative(mNativeHandle, size);
throwIllegalStateExceptionIfNeeded(result);
return size[0];
}
@Override
public int setCaptureSize(int size) throws IllegalStateException {
checkNativeImplIsAvailable();
int result = setCaptureSizeImplNative(mNativeHandle, size);
if (result == OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_STATE) {
throw new IllegalStateException("setCaptureSize() called while unexpected state");
}
throwIllegalStateExceptionIfNeeded(result);
return translateErrorCode(result);
}
@Override
public int setDataCaptureListener(
OnDataCaptureListener listener,
int rate, boolean waveform, boolean fft) {
checkNativeImplIsAvailable();
if (listener == null) {
rate = 0;
waveform = false;
fft = false;
}
int result = setDataCaptureListenerImplNative(
mNativeHandle, rate, waveform, fft);
if (result == OpenSLMediaPlayer.Internal.RESULT_SUCCESS) {
mOnDataCaptureListener = listener;
}
throwIllegalStateExceptionIfNeeded(result);
return translateErrorCode(result);
}
@Override
public int[] getCaptureSizeRange() throws IllegalStateException {
checkNativeImplIsAvailable();
return sGetCaptureSizeRange();
}
@Override
public int getMaxCaptureRate() throws IllegalStateException {
checkNativeImplIsAvailable();
return sGetMaxCaptureRate();
}
@Override
public int getScalingMode() throws IllegalStateException {
checkNativeImplIsAvailable();
int[] mode = mParamIntBuff;
int result = getScalingModeImplNative(mNativeHandle, mode);
throwIllegalStateExceptionIfNeeded(result);
return mode[0];
}
@Override
public int setScalingMode(int mode) throws IllegalStateException {
checkNativeImplIsAvailable();
int result = setScalingModeImplNative(mNativeHandle, mode);
throwIllegalStateExceptionIfNeeded(result);
return translateErrorCode(result);
}
@Override
public int getMeasurementMode() throws IllegalStateException {
checkNativeImplIsAvailable();
int[] mode = mParamIntBuff;
int result = getMeasurementModeImplNative(mNativeHandle, mode);
throwIllegalStateExceptionIfNeeded(result);
return mode[0];
}
@Override
public int setMeasurementMode(int mode) throws IllegalStateException {
checkNativeImplIsAvailable();
int result = setMeasurementModeImplNative(mNativeHandle, mode);
throwIllegalStateExceptionIfNeeded(result);
return translateErrorCode(result);
}
@Override
public int getMeasurementPeakRms(MeasurementPeakRms measurement) {
checkNativeImplIsAvailable();
if (measurement == null)
return ERROR_BAD_VALUE;
int[] measurementBuff = mParamIntBuff;
int result = getMeasurementPeakRmsImplNative(mNativeHandle, measurementBuff);
throwIllegalStateExceptionIfNeeded(result);
measurement.mPeak = measurementBuff[0];
measurement.mRms = measurementBuff[1];
return translateErrorCode(result);
}
//
// Utilities
//
private void checkNativeImplIsAvailable() throws IllegalStateException {
if (mNativeHandle == 0) {
throw new IllegalStateException("Native implemenation handle is not present");
}
}
private static void throwIllegalStateExceptionIfNeeded(int result) {
if (result == OpenSLMediaPlayer.Internal.RESULT_DEAD_OBJECT)
throw new IllegalStateException();
}
private static int translateErrorCode(int err) {
switch (err) {
case OpenSLMediaPlayer.Internal.RESULT_SUCCESS:
return IVisualizer.SUCCESS;
case OpenSLMediaPlayer.Internal.RESULT_INVALID_HANDLE:
return IVisualizer.ERROR_NO_INIT;
case OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_STATE:
case OpenSLMediaPlayer.Internal.RESULT_CONTROL_LOST:
return IVisualizer.ERROR_INVALID_OPERATION;
case OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_ARGUMENT:
return IVisualizer.ERROR_BAD_VALUE;
case OpenSLMediaPlayer.Internal.RESULT_MEMORY_ALLOCATION_FAILED:
case OpenSLMediaPlayer.Internal.RESULT_RESOURCE_ALLOCATION_FAILED:
return IVisualizer.ERROR_NO_MEMORY;
case OpenSLMediaPlayer.Internal.RESULT_DEAD_OBJECT:
return IVisualizer.ERROR_DEAD_OBJECT;
case OpenSLMediaPlayer.Internal.RESULT_ERROR:
case OpenSLMediaPlayer.Internal.RESULT_INTERNAL_ERROR:
case OpenSLMediaPlayer.Internal.RESULT_CONTENT_NOT_FOUND:
case OpenSLMediaPlayer.Internal.RESULT_CONTENT_UNSUPPORTED:
case OpenSLMediaPlayer.Internal.RESULT_IO_ERROR:
case OpenSLMediaPlayer.Internal.RESULT_PERMISSION_DENIED:
case OpenSLMediaPlayer.Internal.RESULT_TIMED_OUT:
case OpenSLMediaPlayer.Internal.RESULT_IN_ERROR_STATE:
default:
return IVisualizer.ERROR;
}
}
//
// JNI binder internal methods
//
private static final int EVENT_TYPE_ON_WAVEFORM_DATA_CAPTURE = 0;
private static final int EVENT_TYPE_ON_FFT_DATA_CAPTURE = 1;
@SuppressWarnings("unchecked")
private static void raiseCaptureEventFromNative(
Object ref, int type, byte[] data, int samplingRate) {
//
// This method is called from the native implementation
//
WeakReference<OpenSLVisualizer> weak_ref = (WeakReference<OpenSLVisualizer>) ref;
OpenSLVisualizer thiz = weak_ref.get();
if (thiz == null)
return;
InternalHandler handler = thiz.mHandler;
if (handler == null)
return;
switch (type) {
case EVENT_TYPE_ON_WAVEFORM_DATA_CAPTURE:
handler.sendMessage(
handler.obtainMessage(
InternalHandler.MSG_ON_WAVEFORM_DATA_CAPTURE,
samplingRate, 0, data));
break;
case EVENT_TYPE_ON_FFT_DATA_CAPTURE:
handler.sendMessage(
handler.obtainMessage(
InternalHandler.MSG_ON_FFT_DATA_CAPTURE,
samplingRate, 0, data));
break;
}
}
private static class InternalHandler extends Handler {
public static final int MSG_ON_WAVEFORM_DATA_CAPTURE = 1;
public static final int MSG_ON_FFT_DATA_CAPTURE = 2;
private WeakReference<OpenSLVisualizer> mHolder;
public InternalHandler(OpenSLVisualizer holder) {
mHolder = new WeakReference<OpenSLVisualizer>(holder);
}
@Override
public void handleMessage(Message msg) {
final OpenSLVisualizer holder = mHolder.get();
if (holder == null)
return;
final OnDataCaptureListener listener = holder.mOnDataCaptureListener;
if (listener == null)
return;
switch (msg.what) {
case MSG_ON_WAVEFORM_DATA_CAPTURE:
listener.onWaveFormDataCapture(holder, (byte[]) msg.obj, msg.arg1);
break;
case MSG_ON_FFT_DATA_CAPTURE:
listener.onFftDataCapture(holder, (byte[]) msg.obj, msg.arg1);
break;
}
}
public void release() {
removeMessages(MSG_ON_FFT_DATA_CAPTURE);
removeMessages(MSG_ON_WAVEFORM_DATA_CAPTURE);
mHolder.clear();
}
}
//
// Native methods
//
private static native long createNativeImplHandle(
long context_handle, WeakReference<OpenSLVisualizer> weak_thiz);
private static native void deleteNativeImplHandle(long handle);
private static native int setEnabledImplNative(long handle, boolean enabled);
private static native int getEnabledImplNative(long handle, boolean[] enabled);
private static native int getSamplingRateImplNative(long handle, int[] samplingRate);
private static native int getCaptureSizeImplNative(long handle, int[] size);
private static native int setCaptureSizeImplNative(long handle, int size);
private static native int getCaptureSizeRangeImplNative(int[] range);
private static native int getFftImplNative(long handle, byte[] fft);
private static native int getWaveformImplNative(long handle, byte[] waveform);
private static native int setDataCaptureListenerImplNative(
long handle, int rate, boolean waveform, boolean fft);
private static native int getMaxCaptureRateImplNative(int[] rate);
private static native int getScalingModeImplNative(long handle, int[] mode);
private static native int setScalingModeImplNative(long handle, int mode);
private static native int getMeasurementModeImplNative(long handle, int[] mode);
private static native int setMeasurementModeImplNative(long handle, int mode);
private static native int getMeasurementPeakRmsImplNative(long handle, int[] measurement);
}