com.hkm.mmedic.contents.HQVisualizerFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.hkm.mmedic.contents.HQVisualizerFragment.java

Source

/*
 *    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.hkm.mmedic.contents;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.ActionBar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.Spinner;
import android.widget.ToggleButton;

import com.hkm.mmedic.R;
import com.hkm.mmedic.framework.AppEvent;
import com.hkm.mmedic.framework.AppEventBus;
import com.hkm.mmedic.model.EventDefs;
import com.hkm.mmedic.model.EventDefs.HQVisualizerNotifyEvents;
import com.hkm.mmedic.model.EventDefs.HQVisualizerReqEvents;
import com.hkm.mmedic.model.EventDefs.PlayerControlNotifyEvents;
import com.hkm.mmedic.model.HQVisualizerStateStore;
import com.hkm.mmedic.utils.ActionBarTileBuilder;
import com.hkm.mmedic.visualizer.BaseAudioVisualizerSurfaceView;
import com.hkm.mmedic.visualizer.HQFftVisualizerSurfaceView;
import com.hkm.mmedic.visualizer.HQWaveformVisualizerSurfaceView;
import com.h6ah4i.android.media.audiofx.IHQVisualizer;

public class HQVisualizerFragment extends ContentsBaseFragment
        implements CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {

    private static final int[] WINDOW_TYPE_VALUES = { IHQVisualizer.WINDOW_RECTANGULAR,
            IHQVisualizer.WINDOW_RECTANGULAR | IHQVisualizer.WINDOW_OPT_APPLY_FOR_WAVEFORM,
            IHQVisualizer.WINDOW_HANN, IHQVisualizer.WINDOW_HANN | IHQVisualizer.WINDOW_OPT_APPLY_FOR_WAVEFORM,
            IHQVisualizer.WINDOW_HAMMING,
            IHQVisualizer.WINDOW_HAMMING | IHQVisualizer.WINDOW_OPT_APPLY_FOR_WAVEFORM,
            IHQVisualizer.WINDOW_BLACKMAN,
            IHQVisualizer.WINDOW_BLACKMAN | IHQVisualizer.WINDOW_OPT_APPLY_FOR_WAVEFORM,
            IHQVisualizer.WINDOW_FLAT_TOP,
            IHQVisualizer.WINDOW_FLAT_TOP | IHQVisualizer.WINDOW_OPT_APPLY_FOR_WAVEFORM, };

    // fields
    private volatile HQWaveformVisualizerSurfaceView mWaveformVisualizerView;
    private volatile HQFftVisualizerSurfaceView mFftVisualizerView;
    private ToggleButton mToggleButtonEnableWaveform;
    private ToggleButton mToggleButtonEnableFft;
    private Spinner mSpinnerWindowType;

    private AppEventReceiver mAppEventReceiver;
    boolean mDuringSetupViews;

    private IHQVisualizer.OnDataCaptureListener mOnDataCaptureListener = new IHQVisualizer.OnDataCaptureListener() {

        @Override
        public void onWaveFormDataCapture(IHQVisualizer visualizer, float[] waveform, int numChannels,
                int samplingRate) {
            HQVisualizerFragment.this.onWaveFormDataCapture(visualizer, waveform, numChannels, samplingRate);
        }

        @Override
        public void onFftDataCapture(IHQVisualizer visualizer, float[] fft, int numChannels, int samplingRate) {
            HQVisualizerFragment.this.onFftDataCapture(visualizer, fft, numChannels, samplingRate);
        }
    };

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_hq_visualizer, container, false);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        obtainViewReferences();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mWaveformVisualizerView = null;
        mFftVisualizerView = null;
        mToggleButtonEnableWaveform = null;
        mToggleButtonEnableFft = null;
    }

    @Override
    public void onStart() {
        super.onStart();

        mAppEventReceiver = new AppEventReceiver(this);
        eventBus().register(mAppEventReceiver);
    }

    @Override
    public void onStop() {
        super.onStop();

        eventBus().unregister(mAppEventReceiver);
        mAppEventReceiver = null;
    }

    @Override
    public void onResume() {
        super.onResume();

        setupViews();
        resumeVisualizerViews();
        updateCaptureSettings();
    }

    @Override
    public void onPause() {
        super.onPause();
        pauseVisualizerViews();
        cleanupVisualizer();
    }

    @Override
    protected void onUpdateActionBarAndOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onUpdateActionBarAndOptionsMenu(menu, inflater);

        ActionBar actionBar = getActionBar();

        actionBar.setTitle(ActionBarTileBuilder.makeTitleString(getActivity(), R.string.title_hq_visualizer));
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        ViewCompat.setAlpha(buttonView, buttonView.isChecked() ? 0.5f : 1.0f);

        if (mDuringSetupViews)
            return;

        switch (buttonView.getId()) {
        case R.id.toggle_button_visualizer_enable_waveform:
            postAppEvent(HQVisualizerReqEvents.SET_WAVEFORM_ENABLED, (isChecked ? 1 : 0), 0);
            break;
        case R.id.toggle_button_visualizer_enable_fft:
            postAppEvent(HQVisualizerReqEvents.SET_FFT_ENABLED, (isChecked ? 1 : 0), 0);
            break;
        }
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        switch (parent.getId()) {
        case R.id.spinner_hq_visualizer_window_type:
            postAppEvent(HQVisualizerReqEvents.SET_WINDOW_TYPE, listPositionToWindowType(position), 0);
            break;
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }

    /* package */void onReceiveAppEvent(AppEvent event) {
        switch (event.category) {
        case EventDefs.Category.NOTIFY_PLAYER_CONTROL:
            onNotifyPlayerControlEvent(event);
            break;
        case EventDefs.Category.NOTIFY_HQ_VISUALIZER:
            onNotifyHQVisualizerEvent(event);
            break;
        }
    }

    private void onNotifyPlayerControlEvent(AppEvent event) {
        switch (event.event) {
        case PlayerControlNotifyEvents.PLAYER_STATE_CHANGED:
            onPlayerStateChanged(event.arg1);
            break;
        }
    }

    private void onNotifyHQVisualizerEvent(AppEvent event) {
        switch (event.event) {
        case HQVisualizerNotifyEvents.WAVEFORM_ENABLED_STATE_UPDATED:
        case HQVisualizerNotifyEvents.FFT_ENABLED_STATE_UPDATED:
            updateCaptureSettings();
            break;
        case HQVisualizerNotifyEvents.WINDOW_TYPE_UPDATED:
            updateWindowType();
            break;
        }
    }

    private void onPlayerStateChanged(int state) {
        if (state == PlayerControlNotifyEvents.STATE_IDLE) {
            updateCaptureSettings();
        }
    }

    private void obtainViewReferences() {
        mToggleButtonEnableWaveform = findToggleButtonByIdAndSetListener(
                R.id.toggle_button_visualizer_enable_waveform);
        mToggleButtonEnableFft = findToggleButtonByIdAndSetListener(R.id.toggle_button_visualizer_enable_fft);
        mSpinnerWindowType = findSpinnerByIdAndSetListener(R.id.spinner_hq_visualizer_window_type);

        mWaveformVisualizerView = (HQWaveformVisualizerSurfaceView) findViewById(
                R.id.surfaceview_visualizer_waveform);
        mFftVisualizerView = (HQFftVisualizerSurfaceView) findViewById(R.id.surfaceview_visualizer_fft);
    }

    private void setupViews() {
        HQVisualizerStateStore state = getStateStore();
        Context context = getActivity();

        mDuringSetupViews = true;

        mToggleButtonEnableWaveform.setChecked(state.isCaptureWaveformEnabled());
        mToggleButtonEnableFft.setChecked(state.isCaptureFftEnabled());

        {
            final ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context,
                    R.array.hq_visualizer_window_type_names, android.R.layout.simple_spinner_item);
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            mSpinnerWindowType.setAdapter(adapter);
        }

        mSpinnerWindowType.setSelection(windowTypeToListPosition(state.getWindowType()));

        mDuringSetupViews = false;
    }

    private static void safeResumeVizualizerView(BaseAudioVisualizerSurfaceView v) {
        if (v != null) {
            v.onResume();
        }
    }

    private static void safePauseVizualizerView(BaseAudioVisualizerSurfaceView v) {
        if (v != null) {
            v.onResume();
        }
    }

    private void resumeVisualizerViews() {
        safeResumeVizualizerView(mWaveformVisualizerView);
        safeResumeVizualizerView(mFftVisualizerView);
    }

    private void pauseVisualizerViews() {
        safePauseVizualizerView(mWaveformVisualizerView);
        safePauseVizualizerView(mFftVisualizerView);
    }

    private void updateCaptureSettings() {
        final IHQVisualizer visualizer = getVisualizer();
        final HQVisualizerStateStore state = getStateStore();

        final boolean captureWaveform = state.isCaptureWaveformEnabled();
        final boolean captureFft = state.isCaptureFftEnabled();

        if (visualizer != null) {
            // stop visualizer
            stopVisualizer(visualizer);

            if (captureFft || captureWaveform) {
                // capturing enabled

                // use maximum rate & size
                int rate = visualizer.getMaxCaptureRate();
                int size = 4096;

                // NOTE: min = 128, max = 32768
                size = Math.max(visualizer.getCaptureSizeRange()[0], size);
                size = Math.min(visualizer.getCaptureSizeRange()[1], size);

                visualizer.setWindowFunction(state.getWindowType());
                visualizer.setCaptureSize(size);
                visualizer.setDataCaptureListener(mOnDataCaptureListener, rate, captureWaveform, captureFft);
            } else {
                // capturing disabled
                visualizer.setDataCaptureListener(null, 0, false, false);
            }

            // start visualizer
            startVisualizer(visualizer, state);
        }
    }

    private void updateWindowType() {
        final IHQVisualizer visualizer = getVisualizer();
        final HQVisualizerStateStore state = getStateStore();

        if (visualizer != null) {
            visualizer.setWindowFunction(state.getWindowType());
        }
    }

    private HQVisualizerStateStore getStateStore() {
        return getAppController().getHQVisualizerStateStore();
    }

    private IHQVisualizer getVisualizer() {
        return getAppController().getHQVisualizer();
    }

    private void cleanupVisualizer() {
        IHQVisualizer visualizer = getVisualizer();

        if (visualizer != null) {
            visualizer.setEnabled(false);

            visualizer.setDataCaptureListener(null, 0, false, false);
        }
    }

    private void startVisualizer(IHQVisualizer visualizer, HQVisualizerStateStore state) {
        boolean waveform = state.isCaptureWaveformEnabled();
        boolean fft = state.isCaptureFftEnabled();

        if (visualizer != null) {
            visualizer.setEnabled(waveform | fft);
        }
    }

    private void stopVisualizer(IHQVisualizer visualizer) {
        if (visualizer != null) {
            visualizer.setEnabled(false);
        }
    }

    /* package */void onWaveFormDataCapture(IHQVisualizer visualizer, float[] waveform, int numChannels,
            int samplingRate) {
        HQWaveformVisualizerSurfaceView view = mWaveformVisualizerView;

        if (view != null) {
            view.updateAudioData(waveform, numChannels, samplingRate);
        }
    }

    /* package */void onFftDataCapture(IHQVisualizer visualizer, float[] fft, int numChannels, int samplingRate) {
        HQFftVisualizerSurfaceView view = mFftVisualizerView;

        if (view != null) {
            view.updateAudioData(fft, numChannels, samplingRate);
        }
    }

    private void postAppEvent(int event, int arg1, float arg2) {
        eventBus().post(new AppEvent(EventDefs.Category.HQ_VISUALIZER, event, arg1, arg2));
    }

    private static class AppEventReceiver extends AppEventBus.Receiver<HQVisualizerFragment> {
        private static final int[] FILTER = new int[] { EventDefs.Category.NOTIFY_PLAYER_CONTROL,
                EventDefs.Category.NOTIFY_HQ_VISUALIZER, };

        public AppEventReceiver(HQVisualizerFragment holder) {
            super(holder, FILTER);
        }

        @Override
        protected void onReceiveAppEvent(HQVisualizerFragment holder, AppEvent event) {
            holder.onReceiveAppEvent(event);
        }
    }

    private static int windowTypeToListPosition(int type) {
        for (int i = 0; i < WINDOW_TYPE_VALUES.length; i++) {
            if (WINDOW_TYPE_VALUES[i] == type) {
                return i;
            }
        }
        return -1;
    }

    private static int listPositionToWindowType(int position) {
        if (position >= 0 && position < WINDOW_TYPE_VALUES.length) {
            return WINDOW_TYPE_VALUES[position];
        } else {
            return WINDOW_TYPE_VALUES[0];
        }
    }
}