/*
* 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.util.Log;
import com.h6ah4i.android.media.audiofx.IHQVisualizer;
import com.h6ah4i.android.media.opensl.OpenSLMediaPlayer;
import com.h6ah4i.android.media.opensl.OpenSLMediaPlayerContext;
import com.h6ah4i.android.media.opensl.OpenSLMediaPlayerNativeLibraryLoader;
public class OpenSLHQVisualizer implements IHQVisualizer {
private static final String TAG = "HQVisualizer";
private long mNativeHandle;
private static final boolean HAS_NATIVE;
private int[] mParamIntBuff = new int[2];
private boolean[] mParamBoolBuff = new boolean[1];
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 OpenSLHQVisualizer(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<OpenSLHQVisualizer>(this));
}
if (mNativeHandle == 0) {
throw new UnsupportedOperationException("Failed to initialize native layer");
}
}
@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);
}
}
@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 getNumChannels() throws IllegalStateException {
checkNativeImplIsAvailable();
final int[] numChannels = mParamIntBuff;
int result = getNumChannelsImplNative(mNativeHandle, numChannels);
throwIllegalStateExceptionIfNeeded(result);
return numChannels[0];
}
@Override
public int getSamplingRate() throws IllegalStateException {
checkNativeImplIsAvailable();
final int[] samplingRate = mParamIntBuff;
int result = getSamplingRateImplNative(mNativeHandle, samplingRate);
throwIllegalStateExceptionIfNeeded(result);
return samplingRate[0];
}
@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 getWindowFunction() throws IllegalStateException {
checkNativeImplIsAvailable();
final int[] windowType = mParamIntBuff;
int result = getWindowFunctionImplNative(mNativeHandle, windowType);
throwIllegalStateExceptionIfNeeded(result);
return windowType[0];
}
@Override
public int setWindowFunction(int windowType) throws IllegalStateException {
checkNativeImplIsAvailable();
int result = setWindowFunctionImplNative(mNativeHandle, windowType);
if (result == OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_STATE) {
throw new IllegalStateException("setWindowFunction() 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();
}
//
// 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 IHQVisualizer.SUCCESS;
case OpenSLMediaPlayer.Internal.RESULT_INVALID_HANDLE:
return IHQVisualizer.ERROR_NO_INIT;
case OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_STATE:
case OpenSLMediaPlayer.Internal.RESULT_CONTROL_LOST:
return IHQVisualizer.ERROR_INVALID_OPERATION;
case OpenSLMediaPlayer.Internal.RESULT_ILLEGAL_ARGUMENT:
return IHQVisualizer.ERROR_BAD_VALUE;
case OpenSLMediaPlayer.Internal.RESULT_MEMORY_ALLOCATION_FAILED:
case OpenSLMediaPlayer.Internal.RESULT_RESOURCE_ALLOCATION_FAILED:
return IHQVisualizer.ERROR_NO_MEMORY;
case OpenSLMediaPlayer.Internal.RESULT_DEAD_OBJECT:
return IHQVisualizer.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 IHQVisualizer.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, float[] data, int numChannels, int samplingRate) {
//
// This method is called from the native implementation
//
WeakReference<OpenSLHQVisualizer> weak_ref = (WeakReference<OpenSLHQVisualizer>) ref;
OpenSLHQVisualizer thiz = weak_ref.get();
if (thiz == null)
return;
OnDataCaptureListener listener = thiz.mOnDataCaptureListener;
if (listener == null)
return;
// HQVisualizer don't use Handler to avoid overhead
switch (type) {
case EVENT_TYPE_ON_WAVEFORM_DATA_CAPTURE:
try {
listener.onWaveFormDataCapture(thiz, data, numChannels, samplingRate);
} catch (Exception e) {
// Ignore all exceptions
}
break;
case EVENT_TYPE_ON_FFT_DATA_CAPTURE:
try {
listener.onFftDataCapture(thiz, data, numChannels, samplingRate);
} catch (Exception e) {
// Ignore all exceptions
}
break;
}
}
//
// Native methods
//
private static native long createNativeImplHandle(
long context_handle, WeakReference<OpenSLHQVisualizer> 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 getNumChannelsImplNative(long handle, int[] numChannels);
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 setDataCaptureListenerImplNative(
long handle, int rate, boolean waveform, boolean fft);
private static native int getMaxCaptureRateImplNative(int[] rate);
private static native int getWindowFunctionImplNative(long handle, int[] windowType);
private static native int setWindowFunctionImplNative(long handle, int windowType);
}